瀏覽代碼

更改导入方案

zhongzewei 6 年之前
父節點
當前提交
539ef6e957

+ 1 - 0
config/gulpConfig.js

@@ -130,6 +130,7 @@ module.exports = {
         'web/building_saas/main/js/views/zlfb_view.js',
         'web/building_saas/main/js/views/installation_fee_view.js',
         'web/building_saas/main/js/views/project_glj_view.js',
+        'web/building_saas/main/js/views/importBills.js',
         'public/web/rpt_tpl_def.js',
         'public/web/treeDataHelper.js',
         'public/web/ztree_common.js',

+ 10 - 10
lib/jquery-ui/jquery-ui.css

@@ -1058,38 +1058,38 @@ a.ui-button:active,
 /* Icons
 ----------------------------------*/
 
-/* states and images */
-.ui-icon {
+ states and images
+/*.ui-icon {
 	width: 16px;
 	height: 16px;
 }
-/*.ui-icon,
+.ui-icon,
 .ui-widget-content .ui-icon {
-	background-image: url("images/ui-icons_444444_256x240.png");
+	background-image: url("./images/ui-icons_444444_256x240.png");
 }
 .ui-widget-header .ui-icon {
-	background-image: url("images/ui-icons_444444_256x240.png");
+	background-image: url("./images/ui-icons_444444_256x240.png");
 }
 .ui-state-hover .ui-icon,
 .ui-state-focus .ui-icon,
 .ui-button:hover .ui-icon,
 .ui-button:focus .ui-icon {
-	background-image: url("images/ui-icons_555555_256x240.png");
+	background-image: url("./images/ui-icons_555555_256x240.png");
 }
 .ui-state-active .ui-icon,
 .ui-button:active .ui-icon {
-	background-image: url("images/ui-icons_ffffff_256x240.png");
+	background-image: url("./images/ui-icons_ffffff_256x240.png");
 }
 .ui-state-highlight .ui-icon,
 .ui-button .ui-state-highlight.ui-icon {
-	background-image: url("images/ui-icons_777620_256x240.png");
+	background-image: url("./images/ui-icons_777620_256x240.png");
 }
 .ui-state-error .ui-icon,
 .ui-state-error-text .ui-icon {
-	background-image: url("images/ui-icons_cc0000_256x240.png");
+	background-image: url("./images/ui-icons_cc0000_256x240.png");
 }
 .ui-button .ui-icon {
-	background-image: url("images/ui-icons_777777_256x240.png");
+	background-image: url("./images/ui-icons_777777_256x240.png");
 }*/
 
 /* positioning */

文件差異過大導致無法顯示
+ 1 - 0
lib/lz-string/lz-string.min.js


文件差異過大導致無法顯示
+ 0 - 54
lib/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js


文件差異過大導致無法顯示
+ 0 - 13
lib/spreadjs/sheets/interop/angular.gc.spread.sheets.10.0.1.min.js


文件差異過大導致無法顯示
+ 0 - 30
lib/spreadjs/sheets/interop/gc.spread.excelio.10.0.1.min.js


文件差異過大導致無法顯示
+ 36 - 0
lib/spreadjs/sheets/interop/gc.spread.excelio.11.1.2.min.js


文件差異過大導致無法顯示
+ 0 - 3
lib/spreadjs/sheets/interop/gc.spread.sheets.migration.10.0.1.min.js


+ 36 - 357
modules/main/controllers/bills_controller.js

@@ -15,6 +15,7 @@ let stdBillsModel = mongoose.model('std_bills_lib_bills');
 let stdBillJobsModel = mongoose.model('std_bills_lib_jobContent');
 let stdBillCharacterModel = mongoose.model('std_bills_lib_itemCharacter');
 import fixedFlag from  '../../common/const/bills_fixed';
+let LZString = require('lz-string');
 const uuidV1 = require('uuid/v1');
 const billType ={
     DXFY:1,//大项费用
@@ -201,19 +202,13 @@ module.exports = {
 
     },
     //导入清单
-    upload: async function(req, res){
+    import: async function(req, res){
         let responseData = {
             err: 0,
             msg: '',
             data: []
         };
-        const allowHeader = ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
-        const uploadOption = {
-            uploadDir: './public'
-        };
-        const form = new multiparty.Form(uploadOption);
-        let uploadFullName;
-        let parseSheetDateA = +new Date();
+        const form = new multiparty.Form();
         form.parse(req, async function(err, fields, files) {
             try{
                 const projectID = fields.projectID !== undefined && fields.projectID.length > 0 ?
@@ -221,65 +216,13 @@ module.exports = {
                 if (projectID <= 0) {
                     throw '参数错误';
                 }
-                const file = files.file !== undefined ? files.file[0] : null;
-                if (err || file === null) {
-                    throw '上传失败';
-                }
-                // 判断类型
-                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;
-                // 重命名文件名
-                uploadFullName = uploadOption.uploadDir + '/' + file.originalFilename;
-                fs.renameSync(file.path, uploadFullName);
-
-                const sheets = excel.parse(uploadFullName);
-                if (sheets[0] === undefined || sheets[0].data === undefined) {
-                    throw 'excel没有对应数据';
-                }
-                //出现错误的表表名,前端提示用
-                let invalidSheets = [];
-                let validSheets = {fbfx: [], jscsxm: [], zzcsxm: []};
-                //获得选择的导入的表及导入位置
-                const uploadWorkBook = fields.uploadWorkBook !== undefined && fields.uploadWorkBook.length > 0 ? JSON.parse(fields.uploadWorkBook[0]) : [];
-                //识别非法和合法表,sheetInfo存储前端勾选的表的位置索引以及选择的导入位置
-                for(let sheetInfo of uploadWorkBook){
-                    let sheet = sheets[sheetInfo.sheetIdx];
-                    if(sheet.data === undefined){
-                        invalidSheets.push(sheet.name);
-                        continue;
-                    }
-                    //获取表的列设置确定导入的格式是否合法(09、广联达)
-                    let colMapping = getColMapping(sheet.data);
-                    if(!isValidSheet(colMapping, fileType)){
-                        invalidSheets.push(sheet.name);
-                        continue;
-                    }
-                    //合法的表
-                    sheet.colMapping = colMapping;
-                    //将合法的表按导入位置分类当做一个表来处理
-                    if(validSheets[sheetInfo.position] !== undefined){
-                        validSheets[sheetInfo.position].push(sheet)
-                    }
+                //导入清单数据
+                let compressData = fields.compressData !== undefined && fields.compressData.length > 0 ?
+                    fields.compressData[0] : null;
+                if(compressData === null){
+                    throw 'excel没有对应数据'
                 }
-                let parseSheetB = +new Date();
-                console.log(`解析表时间: ${parseSheetB - parseSheetDateA}===================================================================`);
-                //合并同类表并提取表的有效数据
-                let validSheetDataA = +new Date();
-                let toImportSheets = [];
-                for(let uploadPosition in validSheets){
-                    let validExcelData = [];
-                    for(let uSheet of validSheets[uploadPosition]){
-                        validExcelData = validExcelData.concat(getValidImportData(uSheet.colMapping, uSheet.data))
-                    }
-                    if(validSheets[uploadPosition].length > 0){
-                        toImportSheets.push({position: uploadPosition, colMapping: validSheets[uploadPosition][0].colMapping, validExcelData: validExcelData});
-                    }
-                }
-                let validSheetDataB = +new Date();
-                console.log(`获取表格有效数据时间: ${validSheetDataB - validSheetDataA}==========================================================`);
+                let importData = JSON.parse(LZString.decompressFromUTF16(compressData));
                 //匹配的清单库
                 let stdDateA = +new Date();
                 const billsLibId = fields.billsLibId !== undefined && fields.billsLibId.length > 0 && fields.billsLibId[0]? parseInt(fields.billsLibId[0]) : null;
@@ -290,33 +233,21 @@ module.exports = {
                     stdCharacters = await stdBillCharacterModel.find({billsLibId: billsLibId, deleted: false});
                 }
                 let stdData = {stdBills: stdBills, stdJobs: stdJobs, stdCharacters: stdCharacters};
-                let stdDateB = +new Date();
-                console.log(`获取标准清单库数据时间: ${stdDateB - stdDateA}`);
                 //导入表
                 let importDateA = +new Date();
-                for(let importData of toImportSheets){
-                    let updateFrontData = await importSheet(importData, req.session.sessionUser.id, projectID, stdData);
-                    if(updateFrontData){
-                        responseData.data.push(updateFrontData);
+                for(let position in importData){
+                    if(importData[position].length > 0){
+                        let updateFrontData = await importSheet(position, importData[position], req.session.sessionUser.id, projectID, stdData);
+                        if(updateFrontData){
+                            responseData.data.push(updateFrontData);
+                        }
                     }
                 }
                 let importDateB = +new Date();
                 console.log(`导入时间: ${importDateB - importDateA}=========================================================================`);
-                if(responseData.data.length === 0){
-                    throw 'excel无有效数据';
-                }
-                if(invalidSheets.length > 0){
-                    let msg = invalidSheets.join('、');
-                    responseData.msg = `${msg},导入失败`;
-                }
-                //删除暂存文件
-                fs.unlink(uploadFullName);
                 res.json(responseData);
             }
             catch (error){
-                if(fs.existsSync(uploadFullName)){
-                    fs.unlink(uploadFullName);
-                }
                 responseData.err = 1;
                 console.log(error);
                 responseData.msg = typeof error === 'object' ? '上传失败' : error;
@@ -327,10 +258,9 @@ module.exports = {
     }
 };
 
-//
-async function importSheet(importData, userID, projectID, stdData){
+async function importSheet(position, excelBills, userID, projectID, stdData){
         //导入位置的有固定行
-        let flag = getImportFlag(importData.position);
+        let flag = getImportFlag(position);
         if(!flag){
             throw 'excel数据错误';
         }
@@ -361,169 +291,19 @@ async function importSheet(importData, userID, projectID, stdData){
             await billsData.model.create(insertFixedBill);
             fixedBill = insertFixedBill;
         }
-        //将excel数据转换成清单树结构数据
-        let insertDatas = parseToBillData(importData.validExcelData, importData.colMapping, fixedBill, projectID, stdData);
-        /*if(insertDatas.length === 0){
-            throw 'excel无有效数据';
-        }*/
-        if(insertDatas.length === 0){
-            return null;
-        }
+        //将excel清单数据转换成完整清单数据(设置ParentID、匹配标准清单库)
+        parseToCompleteBills(excelBills, fixedBill, stdData);
         //删除相关数据
         let deleteDatas = await billsData.deepDeleteBill([fixedBill], userID);
         //新增清单数据
-        await billsData.importBills(insertDatas);
+        await billsData.importBills(excelBills);
         //返回数据以更新前端
         if(insertFixedBill){
-            insertDatas.push(insertFixedBill);
-        }
-        return {fixedBill: fixedBill, insert: {bill: insertDatas, ration: []}, remove: {bill: deleteDatas.bill, ration: deleteDatas.ration}};
-}
-
-//是否是有效的表头列格式,只要含有各表需要的列就行,不严格控制多少列
-function isValidSheet(colMapping, fileType){
-    function hasField(field, all){
-        for(let i of all){
-            if(field === i){
-                return true;
-            }
-        }
-        return false;
-    }
-    let needFields;
-    if(fileType === uploadType.lj){
-        //09表:序号、项目编码、项目名称、项目特征、计量单位、工程量、金额
-        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++;
+            excelBills.push(insertFixedBill);
         }
-    }
-    return hasFieldCount === needFields.length;
+        return {fixedBill: fixedBill, insert: {bill: excelBills, ration: []}, remove: {bill: deleteDatas.bill, ration: deleteDatas.ration}};
 }
 
-//提取excel表头列对应数据
-function getColMapping(sheetData){
-    //获取表头
-    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 colMapping = {};
-    let headRow = getHeadRow(sheetData);
-    for(let c = 0; c < headRow.length; c++){
-        if(headRow[c]){
-            headRow[c] = headRow[c].toString().replace(/\s/g, '');
-            //重复的,只取第一个
-            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;
-}
-function rowExistData(rowData){
-    for(let cData of rowData){
-        if(cData !== undefined && cData !== ''){
-            return true;
-        }
-    }
-    return false;
-}
-//提取excel表数据中的有效数据(去表头表尾,提取其中的excel数据)(根据fixedBill获取栏头占行数)
-function getValidImportData(colMapping, sheetData){
-    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(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, '');
-            //表头
-            if(rData[0] === '序号'){
-                withingD = true;
-                if(fixedBill.name !== '施工组织措施项目'){
-                    r++;
-                }
-                continue;
-            }
-            //表尾
-            else if(rData[0] === '本页小计' || rData[0] === '合计'){
-                withingD = false;
-            }
-        }
-        if(withingD && rowExistData(rData)){
-            validData.push(rData);
-        }*/
-    }
-    return validData;
-}
 
 function getImportFlag(position){
     const fixedItem = {'fbfx': fixedFlag.SUB_ENGINERRING, 'jscsxm': fixedFlag.CONSTRUCTION_TECH, 'zzcsxm': fixedFlag.CONSTRUCTION_ORGANIZATION};
@@ -532,46 +312,22 @@ function getImportFlag(position){
 function isDef(data){
     return typeof data !== 'undefined' && data !== null && data !== '';
 }
-//excel数据转换成清单数据
-function parseToBillData(validData, colMapping, fixedBill, projectID, stdData){
-    let rst = [];
-    let billIdx = {};
-    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){
-        //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
-        let serialNo = removeESC(rData[colMapping.serialNo]);
-        let code = removeESC(rData[colMapping.code]);
-        return !isDef(serialNo) && isDef(code);
-    }
-    //子节点:有序号
-    function isLeaf(rData){
-        let serialNo = removeESC(rData[colMapping.serialNo]);
-        return isDef(serialNo);
-    }
-    //续数据:1. 前数据有效 2.无序号 3.无编码 4.有名称或特征
-    function isExtend(preData, rData){
-        let serialNo = removeESC(rData[colMapping.serialNo]);
-        let code = removeESC(rData[colMapping.code]);
-        let name = removeESC(rData[colMapping.name]);
-        let itemCharacterText = removeESC(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){
-            return billType.BILL;
+//将前端解析生成的清单节点数据完善(ParentID、匹配标准清单)
+function parseToCompleteBills(excelBills, fixedBills, stdData){
+    //设置清单ParentID
+    let rootID = fixedBills ? fixedBills.ID: -1;
+    for(let bills of excelBills){
+        if(bills.nodeType === 'root'){
+            rootID = bills.ID;
+            bills.ParentID = fixedBills.ID;
         }
-        else if(flag === fixedFlag.SUB_ENGINERRING){
-            return isLeaf(rData) ? billType.FX : billType.FB;
+        else {
+            bills.ParentID = rootID;
         }
-        return null;
+        delete bills.nodeType;
+        matchStdBill(bills, stdData);
     }
+
     //excel数据与标准库数据匹配,根据清单前九位编码匹配,匹配成功则获取标准清单对应的工程专业、特征及内容
     function matchStdBill(excelBill, stdData){
         let isMatch = false;
@@ -610,86 +366,9 @@ function parseToBillData(validData, colMapping, fixedBill, projectID, stdData){
             }
         }
         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] === '施工技术措施项目'
-            || fixedBill.flags[0].flag == fixedFlag.CONSTRUCTION_ORGANIZATION && rData[colMapping.name] === '施工组织措施项目'){
-            continue;
+            excelBill.type = billType.BX;
         }
-        //过滤无效数据
-        if(!isRoot(rData) && !isLeaf(rData) && !isExtend(preData, rData)){
-            continue;
-        }
-        if(isExtend(preData, rData)){
-            let preBill = billIdx[preID];
-            if(preBill){
-                //合并续数据
-                preBill.code += rData[colMapping.code] ? rData[colMapping.code] : '';
-                preBill.name += rData[colMapping.name] ? rData[colMapping.name] : '';
-                preBill.itemCharacterText += rData[colMapping.itemCharacterText] ? rData[colMapping.itemCharacterText] : '';
-                preBill.unit += rData[colMapping.unit] ? rData[colMapping.unit] : '';
-                preBill.quantity += rData[colMapping.quantity] ? rData[colMapping.quantity] : '';
-            }
-        }
-        else {
-            let newID = uuidV1();
-            let pID = -1;
-            let preBill = null;
-            if(isRoot(rData)){
-                pID = fixedBill.ID;
-                preBill = billIdx[preRootID];
-            }
-            else if(isLeaf(rData)){
-                pID = preRootID !== -1 ? preRootID : fixedBill.ID;
-                preBill = billIdx[preLeafID];
-            }
-            //set bill data
-            billIdx[newID] = {
-                ID: newID, ParentID: pID, NextSiblingID: -1,
-                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: '',
-                jobContent: [],
-                programID: null,
-                unit: rData[colMapping.unit] ? rData[colMapping.unit] : '',
-                quantity: rData[colMapping.quantity] ? rData[colMapping.quantity] : '',
-                //安全文明
-                flags: fixedBill.flags[0].flag === fixedFlag.CONSTRUCTION_ORGANIZATION && (rData[colMapping.name] === '安全文明施工专项费用' || rData[colMapping.name] === '安全文明施工费') ?
-                    [{fieldName: 'fixed', flag: fixedFlag.SAFETY_CONSTRUCTION}] : [],
-                fees: [],
-                projectID: projectID,
-                type: getBillType(rData, fixedBill.flags[0].flag)};
-            //match stdBill and reset programID、jobContent、itemCharacter
-            matchStdBill(billIdx[newID], stdData);
-            //update preBill NextSibling
-            if(preBill){
-                preBill.NextSiblingID = newID;
-            }
-            //set new preID
-            preID = newID;
-            preRootID = isRoot(rData) ? newID : preRootID;
-            preLeafID = isLeaf(rData) ? newID : preLeafID;
-        }
-    }
-    for(let i in billIdx){
-        rst.push(billIdx[i]);
     }
-    return rst;
 }
 
 async function doBillsOrRationsDelete(data) {

+ 1 - 1
modules/main/routes/bills_route.js

@@ -18,7 +18,7 @@ module.exports = function (app) {
     billsRouter.post('/getSectionInfo', billsController.getSectionInfo);
     billsRouter.post('/reorganizeFBFX', billsController.reorganizeFBFX);
     billsRouter.post('/pasteBlock', billsController.pasteBlock);
-    billsRouter.post('/upload', billsController.upload);
+    billsRouter.post('/import', billsController.import);
     billsRouter.get('/downloadExamp', billsController.downloadExample);
     app.use('/bills', billsRouter);
 };

+ 2 - 1
package.json

@@ -52,7 +52,8 @@
     "socket.io": "^2.0.3",
     "ua-parser-js": "^0.7.14",
     "uuid": "^3.1.0",
-    "wiredep": "^2.2.2"
+    "wiredep": "^2.2.2",
+    "lz-string": "^1.4.4"
   },
   "scripts": {
     "start": "C:\\Users\\mai\\AppData\\Roaming\\npm\\babel-node.cmd server.js"

+ 28 - 0
public/web/common_ajax.js

@@ -34,6 +34,34 @@ var CommonAjax = {
             }
         });
     },
+    compressPost: function (url, data, successCallback, errorCallback) {
+        $.ajax({
+            type:"POST",
+            url: url,
+            data: {'data': LZString.compress(JSON.stringify(data))},
+            dataType: 'json',
+            cache: false,
+            timeout: 50000,
+            success: function(result){
+                if (result.error === 0) {
+                    if (successCallback) {
+                        successCallback(result.data);
+                    }
+                } else {
+                    alert('error: ' + result.message);
+                    if (errorCallback) {
+                        errorCallback();
+                    }
+                }
+            },
+            error: function(jqXHR, textStatus, errorThrown){
+                ajaxErrorInfo(jqXHR, textStatus, errorThrown);
+                if (errorCallback) {
+                    errorCallback();
+                }
+            }
+        });
+    },
     postRationLib: function (url, data, successCallback, errorCallback) {
         $.ajax({
             type:"POST",

+ 7 - 3
web/building_saas/main/html/main.html

@@ -1366,9 +1366,10 @@
                     <button style="margin-top: 2px;" type="button" class="btn btn-primary" id="uploadExample">示例</button>
                    <!-- <div id="uploadSheets" style="display: none; border-top: 1px solid #CED4DA;border-left: 1px solid #CED4DA;border-radius: 5px;margin-top: 5px;"></div>-->
                     <div id="uploadSheets"></div>
+                    <div id="importSpread" style="display: none"></div>
                 </div>
                 <div class="modal-footer">
-                    <a href="javascript:void(0);" class="btn btn-primary" id="uploadConfirm">确定导入</a>
+                    <a href="javascript:void(0);" class="btn btn-primary" id="importConfirm">确定导入</a>
                     <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
                 </div>
             </div>
@@ -1450,8 +1451,9 @@
 
 
         <!-- JS. -->
-        <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
-        <script>GC.Spread.Sheets.LicenseKey =  '<%- LicenseKey %>';</script>
+    <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
+    <script src="/lib/spreadjs/sheets/interop/gc.spread.excelio.11.1.2.min.js"></script>
+    <script>GC.Spread.Sheets.LicenseKey =  '<%- LicenseKey %>';</script>
 
     <script type="text/javascript" src="/lib/jquery-ui/jquery-ui-datepickerCN.js"></script>
     <script type="text/javascript" src="/lib/jquery-contextmenu/jquery.contextMenu.js"></script>
@@ -1463,6 +1465,7 @@
         <!--<script src="/lib/spreadjs/views/common/gc.spread.common.10.0.0.min.js" type="text/javascript"></script>-->
         <script src="/lib/spreadjs/views/plugins/gc.spread.views.gridlayout.10.0.0.min.js" type="text/javascript"></script>
         <script src="/lib/js-xlsx/xlsx.core.min.js"></script>
+        <script src="/lib/lz-string/lz-string.min.js"></script>
         <!-- inject:js -->
         <!--<script type="text/javascript" src="/test/tmp_data/test_ration_calc/ration_calc_base.js"></script>-->
         <script type="text/javascript" src="/web/building_saas/main/js/models/main_consts.js"></script>
@@ -1566,6 +1569,7 @@
         <script type="text/javascript" src='/web/building_saas/main/js/views/zlfb_view.js'></script>
         <script type="text/javascript" src='/web/building_saas/main/js/views/installation_fee_view.js'></script>
         <script type="text/javascript" src='/web/building_saas/main/js/views/project_glj_view.js'></script>
+        <script type="text/javascript" src="/web/building_saas/main/js/views/importBills.js"></script>
         <!--报表-->
         <script type="text/javascript" src="/public/web/rpt_tpl_def.js"></script>
         <script type="text/javascript" src="/public/web/treeDataHelper.js"></script>

+ 480 - 0
web/building_saas/main/js/views/importBills.js

@@ -0,0 +1,480 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2018/8/2
+ * @version
+ */
+
+/*
+* 清单导入模块,前端导入excel,进行数据提取,用lz-string进行压缩上传处理
+* */
+
+const importBills = (function(){
+    //单元格数据是否存在
+    function _isDef(data){
+        return typeof data !== 'undefined' && data !== null && data !== '';
+    }
+    
+    //去除转义字符
+    function _deESC(data) {
+        return _isDef(data) ? data.toString().replace(/[\r,\n,\s,\t]/g, '') : data;
+    }
+
+    //列名对应中文字符
+    const colText = {
+        serialNo: ['序号'],
+        code: ['编码', '项目编码'],
+        name: ['名称', '项目名称'],
+        itemCharacterText: ['特征', '项目特征'],
+        unit: ['单位', '计量单位'],
+        quantity: ['工程量', '项目工程量'],
+        money: ['金额'],
+        quantityDetail: ['工程量明细'],
+        feeDetail: ['费用明细'],
+        summation: ['合计', '本页小计'],
+    };
+
+    //导入位置对应清单固定标记
+    const positionFlag = {
+        fbfx: fixedFlag.SUB_ENGINERRING,
+        jscsxm: fixedFlag.CONSTRUCTION_TECH,
+        zzcsxm: fixedFlag.CONSTRUCTION_ORGANIZATION,
+    };
+
+    //上传类型
+    const uploadType = {
+        lj: 'lj',
+        gld: 'gld',
+    };
+
+    //设置导入表内容(选择导入位置)
+    //@param {Object}workBook
+    function setImportSheetsInfo(sheets){
+        let sheetNames = [];
+        let indexMapping = {};
+        for(let sheetName in sheets){
+            indexMapping[sheets[sheetName]['index']] = sheetName;
+        }
+        let sheetsCount = Object.keys(sheets).length;
+        for(let i = 0; i < sheetsCount; i++){
+            sheetNames.push(indexMapping[i]);
+        }
+        let sheetArea = $('#uploadSheets');
+        $('#uploadSheets').height('');
+        sheetArea.empty();
+        for(let sheetName of sheetNames){
+            let sheetDiv = $(`<div style="margin-left: 5px;margin-top: 5px;" class="input-group form-check"><label class="form-check-label" style="width:270px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis">
+                           <input class="form-check-input" type="checkbox">${sheetName}</label></div>`);
+            sheetDiv.find('input[type="checkbox"]').click(function () {
+                if($('#uploadAlert').is(':visible')){
+                    $('#uploadAlert').hide();
+                }
+            });
+            let sel = $(`<select style="margin-left: 5px; border-radius: .20rem;"><option value="fbfx">分部分项工程</option><option value="zzcsxm">施工组织措施项目</option><option value="jscsxm">施工技术措施项目</option></select>`);
+            if(sheetName.includes('分部分项工程项目清单计价表')){
+                sheetDiv.find('input[type="checkbox"]').prop('checked', true);
+                sel.find('option:eq(0)').prop('selected', true);
+            }
+            else if(sheetName.includes('施工组织措施项目清单计价表')){
+                sheetDiv.find('input[type="checkbox"]').prop('checked', true);
+                sel.find('option:eq(1)').prop('selected', true);
+            }
+            else if(sheetName.includes('施工技术措施项目清单计价表')){
+                sheetDiv.find('input[type="checkbox"]').prop('checked', true);
+                sel.find('option:eq(2)').prop('selected', true);
+            }
+            sheetDiv.append(sel);
+            sheetArea.append(sheetDiv);
+
+        }
+        if(sheetNames.length > 0){
+            sheetArea.show();
+        }
+        if($('#uploadSheets').height() > 250){
+            sheetArea.css('overflow', 'auto');
+            sheetArea.height(250);
+        }
+        else {
+            sheetArea.css('overflow', 'hidden');
+        }
+    }
+
+    //获得选择导入的表信息(表索引及导入位置)
+    //@return {Object}
+    function getImportSheetsInfo(){
+        let rst = [];
+        let sheetArea = $('#uploadSheets');
+        let checkedInputs = sheetArea.find('input:checked');
+        for(let checked of checkedInputs){
+            rst.push({index: $(checked).parent().parent().index(), position: $(checked).parent().next().select().val()})
+        }
+        return rst;
+    }
+
+    function getSheetByIndex(sheets, index){
+        for(let sheetName in sheets){
+            let sheet = sheets[sheetName];
+            if(sheet.index === index){
+                return sheet;
+            }
+        }
+        return null;
+    }
+
+    //提取excel表头列名与列下标映射
+    //@
+    function getColMapping(dataTable){
+        //获取表头
+        function getHeadRow(dataTable){
+            for(let rowIdx in dataTable ){
+                for(let colIdx in dataTable[rowIdx]){
+                    let cellData = dataTable[rowIdx][colIdx]['value'];
+                    if(cellData && _deESC(cellData) === colText.serialNo[0]){
+                        return dataTable[rowIdx];
+                    }
+                }
+            }
+            return {};
+        }
+        //获取需要的表头列与列号对应关系
+        let colMapping = {};
+        let headRow = getHeadRow(dataTable);
+        for(let colIdx in headRow){
+            let cellData = headRow[colIdx]['value'];
+            if(!_isDef(cellData)){
+                continue;
+            }
+            //序号
+            if(colMapping.serialNo === undefined && cellData === colText.serialNo[0]){
+                colMapping.serialNo = colIdx;
+            }
+            //编码
+            else if(colMapping.code === undefined && (cellData === colText.code[0] || cellData === colText.code[1])){
+                colMapping.code = colIdx;
+            }
+            //名称
+            else if(colMapping.name === undefined && (cellData === colText.name[0] || cellData === colText.name[1])){
+                colMapping.name = colIdx;
+            }
+            //项目特征
+            else if(colMapping.itemCharacterText === undefined && (cellData === colText.itemCharacterText[0] || cellData === colText.itemCharacterText[1])){
+                colMapping.itemCharacterText = colIdx;
+            }
+            //单位
+            else if(colMapping.unit === undefined && (cellData === colText.unit[0] || cellData === colText.unit[1])){
+                colMapping.unit = colIdx;
+            }
+            //工程量
+            else if(colMapping.quantity === undefined && (cellData === colText.quantity[0] || cellData === colText.quantity[1])){
+                colMapping.quantity = colIdx;
+            }
+            //金额
+            else if(colMapping.money === undefined && cellData.includes(colText.money[0])){
+                colMapping.money = colIdx;
+            }
+            //工程量明细
+            else if(colMapping.quantityDetail === undefined && cellData === colText.quantityDetail[0]){
+                colMapping.quantityDetail = colIdx;
+            }
+            //费用明细
+            else if(colMapping.feeDetail === undefined && cellData === colText.feeDetail[0]){
+                colMapping.feeDetail = colIdx;
+            }
+        }
+        return colMapping;
+    }
+
+    //是否是有效的表头列格式,只要含有各表需要的列就行,不严格控制多少列
+    function isValidSheet(colMapping, fileType){
+        function hasField(field, all){
+            for(let i of all){
+                if(field === i){
+                    return true;
+                }
+            }
+            return false;
+        }
+        let needFields;
+        if(fileType === uploadType.lj){
+            //09表:序号、项目编码、项目名称、项目特征、计量单位、工程量、金额
+            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;
+    }
+
+    //获取要无效和有效导入表
+    //@param {Array}importSheetInfo 勾选要导入的表
+    function getImportSheets(sheets, sheetsInfo, fileType){
+        let rst = {invalidSheets: [], validSheets: {fbfx: [], jscsxm: [], zzcsxm: []}};
+
+        for(let sheetInfo of sheetsInfo){
+            let sheet = getSheetByIndex(sheets, sheetInfo.index);
+            if(!sheet){
+                continue;
+            }
+            //没有数据
+            if(!sheet.data.dataTable){
+                rst.invalidSheets.push(sheet.name);
+                continue;
+            }
+            //获取表的列设置确定导入的格式是否合法(09、广联达)
+            let colMapping = getColMapping(sheet.data.dataTable);
+            if(!isValidSheet(colMapping, fileType)){
+                rst.invalidSheets.push(sheet.name);
+                continue;
+            }
+            //合法的表
+            sheet.colMapping = colMapping;
+            //将合法的表按导入位置分类当做一个表来处理
+            if(rst.validSheets[sheetInfo.position] !== undefined){
+                rst.validSheets[sheetInfo.position].push(sheet)
+            }
+        }
+        return rst;
+    }
+
+    //行存在数据
+    function rowExistData(rowData){
+        for(let colIdx in rowData){
+            let colData = rowData[colIdx]['value'];
+            if(_isDef(colData)){
+                return true;
+            }
+        }
+        return false;
+    }
+
+    //提取excel表数据中的有效数据(去表头表尾,提取其中的excel数据)(根据fixedBill获取栏头占行数)
+    function getValidImportData(colMapping, sheetData){
+        let withingD = false;
+        let validData = [];
+        function isHead(rData){
+            return rData[colMapping.serialNo] && _deESC(rData[colMapping.serialNo]['value']) === colText.serialNo[0];
+        }
+        function isTail(rowData){
+            for(let colIdx in rowData){
+                let colData = rowData[colIdx]['value'];
+                if(colData){
+                    let trimColData= _deESC(colData);
+                    if(trimColData === colText.summation[0] || trimColData === colText.summation[1]){
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+        for(let rowIdx in sheetData){
+            let rowData = sheetData[rowIdx];
+            if(isHead(rowData)){
+                withingD = true;
+                continue;
+            }
+            else if(isTail(rowData)){
+                withingD = false;
+            }
+            if(withingD && rowExistData(rowData)){
+                validData.push(rowData);
+            }
+        }
+        return validData;
+    }
+
+    //excel数据转换成清单数据
+    function parseToBillData(validData, colMapping, flag, projectID){
+        let rst = [];
+        let billIdx = {};
+        let preRootID = -1,
+            preLeafID = -1,
+            preID = -1;
+        //父节点:1.无序号 2有编码
+        function isRoot(rData){
+            //序号和编码去除转义字符(有的表格单元格看起来是没数据,实际含有\r,\n等数据)
+            let serialNo = rData[colMapping.serialNo] ? _deESC(rData[colMapping.serialNo]['value']) : '';
+            let code = rData[colMapping.code] ? _deESC(rData[colMapping.code]['value']) : '';
+            return !_isDef(serialNo) && _isDef(code);
+        }
+        //子节点:有序号
+        function isLeaf(rData){
+            let serialNo = rData[colMapping.serialNo] ? _deESC(rData[colMapping.serialNo]['value']) : '';
+            return _isDef(serialNo);
+        }
+        //续数据:1. 前数据有效 2.无序号 3.无编码 4.有名称或特征
+        function isExtend(preData, rData){
+            let serialNo = rData[colMapping.serialNo] ? _deESC(rData[colMapping.serialNo]['value']) : '';
+            let code = rData[colMapping.code] ? _deESC(rData[colMapping.code]['value']) : '';
+            let name = rData[colMapping.name] ? _deESC(rData[colMapping.name]['value']) : '';
+            let itemCharacterText = rData[colMapping.itemCharacterText] ? _deESC(rData[colMapping.itemCharacterText]['value']) : '';
+            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){
+                return billType.BILL;
+            }
+            else if(flag === fixedFlag.SUB_ENGINERRING){
+                return isLeaf(rData) ? billType.FX : billType.FB;
+            }
+            return null;
+        }
+        //excel数据与标准库数据匹配,根据清单前九位编码匹配,匹配成功则获取标准清单对应的工程专业、特征及内容
+        /*function matchStdBill(excelBill, stdData){
+            let isMatch = false;
+            let regExp = /^\d{12}$/g;
+            if(regExp.test(excelBill.code)){
+                let nineCode = excelBill.code.substr(0, 9);
+                for(let stdBill of stdData.stdBills){
+                    //set programID
+                    if(nineCode == stdBill.code){
+                        isMatch = true;
+                        excelBill.programID = stdBill.engineering ? stdBill.engineering : null;
+                        excelBill.billsLibId = stdBill.billsLibId ? stdBill.billsLibId : null;
+                        //set jobContent and itemCharacter
+                        let tempJob = [], tempCharacter = [];
+                        for(let billJob of stdBill.jobs){
+                            for(let stdJob of stdData.stdJobs) {
+                                if (billJob.id == stdJob.id) {
+                                    tempJob.push({isChecked: false, serialNo: billJob.serialNo, content: stdJob.content});
+                                }
+                            }
+                        }
+                        for(let billCharacter of stdBill.items){
+                            for(let stdCharacter of stdData.stdCharacters){
+                                if(billCharacter.id == stdCharacter.id){
+                                    let eigenvalue = [];
+                                    for(let eValue of stdCharacter.itemValue){
+                                        eigenvalue.push({isSelected: false, value: eValue.value});
+                                    }
+                                    tempCharacter.push({isChecked: false, serialNo: billCharacter.serialNo, character: stdCharacter.content, eigenvalue: eigenvalue});
+                                }
+                            }
+                        }
+                        excelBill.jobContent = tempJob;
+                        excelBill.itemCharacter = tempCharacter;
+                    }
+                }
+            }
+            if(!isMatch && excelBill.type === billType.FX){//分项不为空,同时在标准清单中不匹配,则识别为补项
+                excelBill.type = billType.BX;
+            }
+        }*/
+        for(let r = 0; r < validData.length; r++){
+            let preData = validData[r-1],
+                rData = validData[r];
+            if(flag == fixedFlag.CONSTRUCTION_TECH && rData[colMapping.name] && rData[colMapping.name]['value'] === '施工技术措施项目'
+                || flag == fixedFlag.CONSTRUCTION_ORGANIZATION && rData[colMapping.name] && rData[colMapping.name]['value'] === '施工组织措施项目'){
+                continue;
+            }
+            //过滤无效数据
+            if(!isRoot(rData) && !isLeaf(rData) && !isExtend(preData, rData)){
+                continue;
+            }
+            if(isExtend(preData, rData)){
+                let preBill = billIdx[preID];
+                if(preBill){
+                    //合并续数据
+                    preBill.code += rData[colMapping.code] && rData[colMapping.code]['value'] ? rData[colMapping.code]['value'] : '';
+                    preBill.name += rData[colMapping.name] && rData[colMapping.name]['value'] ? rData[colMapping.name]['value'] : '';
+                    preBill.itemCharacterText += rData[colMapping.itemCharacterText] && rData[colMapping.itemCharacterText]['value'] ? rData[colMapping.itemCharacterText]['value'] : '';
+                    preBill.unit += rData[colMapping.unit] && rData[colMapping.unit]['value'] ? rData[colMapping.unit]['value'] : '';
+                    preBill.quantity += rData[colMapping.quantity] && rData[colMapping.quantity]['value'] ? rData[colMapping.quantity]['value'] : '';
+                }
+            }
+            else {
+                let newID = uuid.v1();
+                let pID = -1;
+                let preBill = null;
+                let nodeType = 'root';//后端以此标记来设置ParentID
+                if(isRoot(rData)){
+
+                    //pID = 'fixedBillID';
+                    preBill = billIdx[preRootID];
+                }
+                else if(isLeaf(rData)){
+                    nodeType = 'leaf';
+                    //pID = preRootID !== -1 ? preRootID : fixedBill.ID;
+                    preBill = billIdx[preLeafID];
+                }
+                //set bill data
+                billIdx[newID] = {
+                    nodeType: nodeType,
+                    ID: newID, ParentID: pID, NextSiblingID: -1,
+                    code: rData[colMapping.code] && rData[colMapping.code]['value'] ? _deESC(rData[colMapping.code]['value']) : '',
+                    name: rData[colMapping.name] && rData[colMapping.name]['value'] ? _deESC(rData[colMapping.name]['value']) : '',
+                    itemCharacterText: rData[colMapping.itemCharacterText] && rData[colMapping.itemCharacterText]['value'] ? rData[colMapping.itemCharacterText]['value'] : '',
+                    itemCharacter: [],
+                    jobContentText: '',
+                    jobContent: [],
+                    programID: null,
+                    unit: rData[colMapping.unit] && rData[colMapping.unit]['value'] ? rData[colMapping.unit]['value'] : '',
+                    quantity: rData[colMapping.quantity] && rData[colMapping.quantity]['value'] ? rData[colMapping.quantity]['value'] : '',
+                    //安全文明
+                    flags: flag === fixedFlag.CONSTRUCTION_ORGANIZATION && (rData[colMapping.name] && (rData[colMapping.name]['value'] === '安全文明施工专项费用' || rData[colMapping.name]['value'] === '安全文明施工费')) ?
+                        [{fieldName: 'fixed', flag: fixedFlag.SAFETY_CONSTRUCTION}] : [],
+                    fees: [],
+                    projectID: projectID,
+                    type: getBillType(rData, flag)};
+                //match stdBill and reset programID、jobContent、itemCharacter
+                //matchStdBill(billIdx[newID], stdData);
+                //update preBill NextSibling
+                if(preBill){
+                    preBill.NextSiblingID = newID;
+                }
+                //set new preID
+                preID = newID;
+                preRootID = isRoot(rData) ? newID : preRootID;
+                preLeafID = isLeaf(rData) ? newID : preLeafID;
+            }
+        }
+        for(let i in billIdx){
+            rst.push(billIdx[i]);
+        }
+        return rst;
+    }
+
+    function getImportData(validSheets, projectID){
+        let rst = {fbfx: [], jscsxm: [], zzcsxm: []};
+        let validSheetsDatas = [];
+        for(let uploadPosition in validSheets){
+            let validExcelData = [];
+            for(let uSheet of validSheets[uploadPosition]){
+                validExcelData = validExcelData.concat(getValidImportData(uSheet.colMapping, uSheet.data.dataTable));
+            }
+            if(validSheets[uploadPosition].length > 0){
+                validSheetsDatas.push({position: uploadPosition, colMapping: validSheets[uploadPosition][0].colMapping, validExcelData: validExcelData});
+            }
+        }
+        console.log(validSheetsDatas);
+        for(let validSheetData of validSheetsDatas){
+            if(validSheetData.validExcelData.length > 0){
+                rst[validSheetData.position] = parseToBillData(validSheetData.validExcelData, validSheetData.colMapping, positionFlag[validSheetData.position], projectID);
+            }
+        }
+        console.log(rst);
+        return rst;
+    }
+
+    function excelHasValidBills(importBillsData){
+        for(let i in importBillsData){
+            if(importBillsData[i].length > 0){
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+
+    return {setImportSheetsInfo, getImportSheetsInfo, getImportSheets, getImportData, excelHasValidBills}
+})();

+ 2 - 2
web/building_saas/main/js/views/project_info.js

@@ -31,8 +31,8 @@ var projectInfoObj = {
                 <span class="text-muted px-1">\</span>
                 <span data-toggle="tooltip" data-placement="bottom" data-original-title="${engName}"><i class="fa fa-cube"></i>...</span>
                 <span class="text-muted px-1">\</span>
-                <span><i class="fa fa-sticky-note-o"></i></span>
-                <span data-toggle="tooltip" data-placement="bottom" data-original-title="${proj.name}"> <span class="text-truncate float-right">&nbsp;${proj.name}</span></span>`;
+                 <span><i class="fa fa-sticky-note-o"></i></span>
+                <span class="text-truncate float-right"  data-toggle="tooltip" data-placement="bottom" data-original-title="${proj.name}">&nbsp;${proj.name}</span>`;
             fullPath.push(newHtml);
 
         }

+ 67 - 108
web/building_saas/main/js/views/project_view.js

@@ -2452,128 +2452,86 @@ $('#uploadGld').click(function () {
    fileType = uploadType.gld;
 });
 
-//设置导入表内容
-function setUploadSheets(sheetNames){
-    let sheetArea = $('#uploadSheets');
-    $('#uploadSheets').height('');
-    sheetArea.empty();
-    for(let sheetName of sheetNames){
-        let sheetDiv = $(`<div style="margin-left: 5px;margin-top: 5px;" class="input-group form-check"><label class="form-check-label" style="width:270px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis">
-                           <input class="form-check-input" type="checkbox">${sheetName}</label></div>`);
-        sheetDiv.find('input[type="checkbox"]').click(function () {
-            if($('#uploadAlert').is(':visible')){
-                $('#uploadAlert').hide();
-            }
-        });
-        let sel = $(`<select style="margin-left: 5px; border-radius: .20rem;"><option value="fbfx">分部分项工程</option><option value="zzcsxm">施工组织措施项目</option><option value="jscsxm">施工技术措施项目</option></select>`);
-        if(sheetName.includes('分部分项工程项目清单计价表')){
-            sheetDiv.find('input[type="checkbox"]').prop('checked', true);
-            sel.find('option:eq(0)').prop('selected', true);
-        }
-        else if(sheetName.includes('施工组织措施项目清单计价表')){
-            sheetDiv.find('input[type="checkbox"]').prop('checked', true);
-            sel.find('option:eq(1)').prop('selected', true);
-        }
-        else if(sheetName.includes('施工技术措施项目清单计价表')){
-            sheetDiv.find('input[type="checkbox"]').prop('checked', true);
-            sel.find('option:eq(2)').prop('selected', true);
-        }
-        sheetDiv.append(sel);
-        sheetArea.append(sheetDiv);
-
-    }
-    if(sheetNames.length > 0){
-        sheetArea.show();
-    }
-    if($('#uploadSheets').height() > 250){
-        console.log('aa');
-        sheetArea.css('overflow', 'auto');
-        sheetArea.height(250);
-    }
-    else {
-        sheetArea.css('overflow', 'hidden');
-    }
-}
-//获得选择导入的表信息(表索引及导入位置)
-function getUploadSheets(){
-    let rst = [];
-    let sheetArea = $('#uploadSheets');
-    let checkedInputs = sheetArea.find('input:checked');
-    for(let checked of checkedInputs){
-        rst.push({sheetIdx: $(checked).parent().parent().index(), position: $(checked).parent().next().select().val()})
-    }
-    return rst;
-}
-
-//选择要导入的excel文件
+let importJson = null;
+let importSpread = null;
+//选择导入的excel文件
 $('#customFile').change(function () {
-    let file = $(this)[0];
-    if(file.files.length > 0){
-        $('.custom-file-label').text(`${file.files[0].name}`);
-        $('#uploadAlert').hide();
-        //读取各个表及表名
-        $.bootstrapLoading.start();
-        $('#loadingPage').css('z-index', '2000');
-        let fileReader = new FileReader();
-        fileReader.onload = function(ev) {
-            try {
-                let data = ev.target.result;
-                // 以二进制流方式读取得到整份excel表格对象
-                let workbook = XLSX.read(data, {type: 'binary'});
-                // 遍历每张表读取
-                let sheetNames = [];
-                for (let sheetName in workbook.Sheets) {
-                    sheetNames.push(sheetName);
-                }
-                setUploadSheets(sheetNames);
-                $.bootstrapLoading.end();
-            } catch (e) {
-                console.log(e);
-                $.bootstrapLoading.end();
-                alert('文件类型不正确');
-                return;
-            }
-        };
-        // 以二进制方式打开文件
-        fileReader.readAsBinaryString(file.files[0]);
-    }
-    else{
-        $('.custom-file-label').text(`请选择上传文件`);
-    }
+   let file = $(this)[0];
+   let excelFile = file.files[0];
+    $('#uploadAlert').hide();
+   if(excelFile) {
+       let xlsReg = /xls$/g;
+       if(excelFile.name && xlsReg.test(excelFile.name)){
+           showUploadAlert(false, '请选择xlsx文件');
+           $(this).val('');
+           return;
+       }
+       $('.custom-file-label').text(`${file.files[0].name}`);
+       $('#uploadAlert').hide();
+       $.bootstrapLoading.start();
+       $('#loadingPage').css('z-index', '2000');
+       //前端解析excel数据
+       if(importJson){
+           importJson = null;
+       }
+       importSpread = new GC.Spread.Sheets.Workbook($('#importSpread')[0]);
+       let excelIo = new GC.Spread.Excel.IO();
+       let sDate = +new Date();
+       excelIo.open(excelFile, function (json) {
+           importJson = json;
+           //读取各个表及表名
+           importBills.setImportSheetsInfo(importJson.sheets);
+           console.log(`解析Excel文件时间:${+new Date() - sDate}`);
+           $.bootstrapLoading.end();
+       }, function (e) {
+           $.bootstrapLoading.end();
+           alert(e.errorMessage);
+       });
+   }
 });
+
 //线上nginx请求体最大设置为100m
 //从excel导入清单
-$('#uploadConfirm').click(function () {
+$('#importConfirm').click(function () {
     let me = this;
     $(me).addClass('disabled');
     try{
+        $.bootstrapLoading.start();
+        $('#loadingPage').css('z-index', '2000');
         let formData = new FormData();
         let file = $('#customFile')[0];
         if(file.files.length <= 0){
             throw '未选择文件';
         }
-        formData.append('file', file.files[0]);
         let projectID = scUrlUtil.GetQueryString('project');
         if(!projectID || projectID <= 0){
             throw '项目数据出错';
         }
-        //导入表类型(09lj、广联达gld)
-        formData.append('fileType', fileType);
-        //选择的表及导入位置
-        let uploadWorkBook = getUploadSheets();
-        if(uploadWorkBook.length === 0){
-            throw '请选择要导入的表';
-        }
-        formData.append('uploadWorkBook', JSON.stringify(uploadWorkBook));
+        let sDate = +new Date();
         formData.append('projectID', projectID);
         //要去匹配的清单库(第一个)
         let matchBillLibId = projectInfoObj.projectInfo.engineeringInfo.bill_lib.length > 0 ? projectInfoObj.projectInfo.engineeringInfo.bill_lib[0].id : null;
         formData.append('billsLibId', matchBillLibId);
-        let uploadS = +new Date();
-        $.bootstrapLoading.start();
-        $('#loadingPage').css('z-index', '2000');
+        //选择的表及导入位置
+        let importSheetsInfo = importBills.getImportSheetsInfo();
+        if(importSheetsInfo.length === 0){
+            throw '请选择要导入的表';
+        }
+        let importSheets = importBills.getImportSheets(importJson.sheets, importSheetsInfo, fileType);
+        console.log(`importSheets`);
+        console.log(importSheets);
+        let invalidSheetsInfo = importSheets.invalidSheets.join('、');
+        //获取同类表的有效数据
+        let importBillsData = importBills.getImportData(importSheets.validSheets, projectID);
+        if(!importBills.excelHasValidBills(importBillsData)){
+            throw 'excel无有效数据'
+        }
+        let compressData = LZString.compressToUTF16(JSON.stringify(importBillsData));
+        formData.append('compressData', compressData);
+        let eDate = +new Date();
+        console.log(`解析excel数据时间:${eDate - sDate}`);
         $.ajax({
-            url: '/bills/upload',
+            url: '/bills/import',
             type: 'POST',
             data: formData,
             cache: false,
@@ -2581,12 +2539,9 @@ $('#uploadConfirm').click(function () {
             processData: false,
             success: function(response){
                 if (response.err === 0) {
-                    const message = response.msg !== undefined ? response.msg : '';
-                    if (message !== '') {
-                        alert(message);
+                    if(invalidSheetsInfo !== ''){
+                        alert(`${invalidSheetsInfo},导入失败。`);
                     }
-                    let uploadE = +new Date();
-                    console.log(`导入时间:${uploadE-uploadS}`);
                     // 成功则关闭窗体
                     $('#import').modal("hide");
                     //更新前端
@@ -2609,11 +2564,14 @@ $('#uploadConfirm').click(function () {
         });
     }
     catch (err){
-        //alert(err);
         showUploadAlert(false, err);
         $(me).removeClass('disabled');
+        if($.bootstrapLoading.isLoading()){
+            $.bootstrapLoading.end();
+        }
     }
 });
+
 function showUploadAlert(success, msg){
     if(!success){
         $('#uploadAlert').removeClass('alert-success');
@@ -2704,6 +2662,7 @@ function doAfterImport(resData){
         }
         //如果清单未锁定,导入后锁定清单
         if(!projectInfoObj.projectInfo.property.lockBills){
+            console.log('enterLockBillsClick');
             $("a[name='lockBills']").click();
         }
         $.bootstrapLoading.end();
@@ -2795,7 +2754,7 @@ function disableTools(){
     $('#use-to-all').addClass('disabled');
     $('.bottom-tools').remove();
     //导入
-    $('#uploadConfirm').addClass('disabled');
+    $('#importConfirm').addClass('disabled');
     //选项
     $('#generalOpts1').prop('disabled', 'disabled');
     $('#generalOpts2').prop('disabled', 'disabled');

+ 3 - 3
web/building_saas/pm/html/project-management.html

@@ -317,12 +317,12 @@
                     </div>
                     <div style="margin-top: 15px;" class="input-group">
                         <label style="margin-top: 8px;">计价规则</label>
-                        <select style="margin-left: 5px; border-radius: .25rem;" class="form-control" id="valuation"><option value="">请选择计价规则</option></select>
+                        <select style="margin-left: 5px; border-radius: .25rem;" class="form-control" id="valuation"></select>
                     </div>
                     <span class="form-text text-danger" id="valuation-info" style="display: none;">请选择计价规则</span>
                     <div style="margin-top: 15px;" class="input-group">
                         <label style="margin-top: 8px;">工程专业</label>
-                        <select style="margin-left: 5px; border-radius: .25rem;" class="form-control" id="tender-engineering"><option value="">请选择工程专业</option></select>
+                        <select style="margin-left: 5px; border-radius: .25rem;" class="form-control" id="tender-engineering"></select>
                     </div>
                     <span class="form-text text-danger" id="engineering-info" style="display: none;">请选择工程专业</span>
                     <div style="margin-top: 15px;" class="input-group" id="taxType_div">
@@ -334,7 +334,7 @@
                     </div>
                     <div style="margin-top: 15px;" class="input-group">
                         <label style="margin-top: 8px;">计算程序</label>
-                        <select style="margin-left: 5px; border-radius: .25rem;" class="form-control" id="tender-calcProgram"><option value="">请选择计算程序</option></select>
+                        <select style="margin-left: 5px; border-radius: .25rem;" class="form-control" id="tender-calcProgram"></select>
                     </div>
 
                     <span class="form-text text-danger" id="calcProgram-info" style="display: none;">请选择计算程序</span>

+ 3 - 3
web/building_saas/pm/js/pm_newMain.js

@@ -1158,7 +1158,7 @@ $(document).ready(function() {
     $("input[name='valuation_type']").click(function() {
         let type = $(this).val();
         let targetData = type === 'bill' ? JSON.parse(billValuation) : JSON.parse(rationValuation);
-        let html = '<option value="">请选择计价规则</option>';
+        let html = '';
         for(let i = targetData.length - 1; i >=0; i--){
             let valuation = targetData[i];
             if (valuation === null) {
@@ -1563,7 +1563,7 @@ $(document).ready(function() {
 
     function getStdCalcProgramFiles(){
         function getStdCPFilesHtml(taxGroups) {
-            let result = '<option value="">请选择计算程序</option>';
+            let result = '';
             if (taxGroups.length <= 0) {
                 return result;
             };
@@ -2680,7 +2680,7 @@ function GetTargetTreeNode(zTreeObj) {
  * @return {String}
  */
 function getEngineeringHtml(engineeringList) {
-    let result = '<option value="">请选择对应的工程专业</option>';
+    let result = '';
     if (engineeringList.length <= 0) {
         return result;
     }

+ 12 - 7
web/common/html/header.html

@@ -6,13 +6,18 @@
     <div  class="navbar-text navbar-crumb px-1" id="fullpath">
 
     </div>
-<!--    <ul class="nav navbar-nav">
-        <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>
+   <!-- <ul class="nav navbar-nav px-1">
+        <li class="nav-item">
+            <a class="nav-link" href="#" aria-expanded="false" data-toggle="modal" data-target="#poj-set"><i class="fa fa-cog"></i> 项目属性</a>
+        </li>
+        <li class="nav-item">
+                        <span class="dropdown">
+                            <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown"><i class="fa  fa-cloud-upload"></i> 导入</a>
+                                <div class="dropdown-menu">
+                                    <a class="dropdown-item" href="#import" data-toggle="modal" data-target="#import">导入09表Excel清单</a>
+                                    <a class="dropdown-item" href="#">导入广联达算量Excel清单</a>
+                                </div>
+                        </span>
         </li>
     </ul>-->
     <div id="testDisplay" style="color:#ff7e0e;">&nbsp;&nbsp;</div>