فهرست منبع

Merge branch '1.0.0_online' of http://smartcost.f3322.net:3000/SmartCost/ConstructionCost into 1.0.0_online

Conflicts:
	public/web/tree_sheet/tree_sheet_helper.js
zhangweicheng 6 سال پیش
والد
کامیت
1a33537407
36فایلهای تغییر یافته به همراه969 افزوده شده و 698 حذف شده
  1. 1 0
      config/gulpConfig.js
  2. 12 12
      lib/jquery-ui/jquery-ui.css
  3. 1 0
      lib/lz-string/lz-string.min.js
  4. 0 54
      lib/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js
  5. 0 13
      lib/spreadjs/sheets/interop/angular.gc.spread.sheets.10.0.1.min.js
  6. 0 30
      lib/spreadjs/sheets/interop/gc.spread.excelio.10.0.1.min.js
  7. 36 0
      lib/spreadjs/sheets/interop/gc.spread.excelio.11.1.2.min.js
  8. 0 3
      lib/spreadjs/sheets/interop/gc.spread.sheets.migration.10.0.1.min.js
  9. 1 1
      modules/complementary_ration_lib/models/compleRationModel.js
  10. 36 357
      modules/main/controllers/bills_controller.js
  11. 1 1
      modules/main/routes/bills_route.js
  12. 15 25
      modules/reports/controllers/rpt_tpl_controller.js
  13. 13 7
      modules/reports/facade/rpt_tpl_tree_node_facade.js
  14. 1 0
      modules/reports/routes/rpt_tpl_router.js
  15. 107 10
      modules/reports/rpt_component/jpc_flow_tab.js
  16. 5 0
      modules/reports/util/rpt_excel_util.js
  17. 2 5
      modules/reports/util/rpt_font_util.js
  18. 2 1
      package.json
  19. 28 0
      public/web/common_ajax.js
  20. 2 1
      public/web/rpt_value_define.js
  21. 34 5
      public/web/sheet/sheet_data_helper.js
  22. 9 3
      public/web/tree_sheet/tree_sheet_helper.js
  23. 8 7
      test/unit/reports/test_rpt_test_template.js
  24. 18 11
      web/building_saas/main/html/main.html
  25. 12 2
      web/building_saas/main/js/models/calc_program.js
  26. 17 2
      web/building_saas/main/js/models/ration.js
  27. 482 0
      web/building_saas/main/js/views/importBills.js
  28. 10 8
      web/building_saas/main/js/views/main_tree_col.js
  29. 2 2
      web/building_saas/main/js/views/project_info.js
  30. 77 117
      web/building_saas/main/js/views/project_view.js
  31. 2 0
      web/building_saas/main/js/views/std_billsGuidance_lib.js
  32. 4 2
      web/building_saas/main/js/views/std_bills_lib.js
  33. 5 4
      web/building_saas/main/js/views/std_ration_lib.js
  34. 3 3
      web/building_saas/pm/html/project-management.html
  35. 9 3
      web/building_saas/pm/js/pm_newMain.js
  36. 14 9
      web/common/html/header.html

+ 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',

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

@@ -979,7 +979,7 @@ a.ui-button:focus {
 .ui-widget-header .ui-state-active,
 a.ui-button:active,
 .ui-button:active,
-.ui-button.ui-state-active:hover {
+/*.ui-button.ui-state-active:hover {
 	border: 1px solid #003eff;
 	background: #007fff;
 	font-weight: normal;
@@ -989,7 +989,7 @@ a.ui-button:active,
 .ui-state-active .ui-icon-background {
 	border: #003eff;
 	background-color: #ffffff;
-}
+}*/
 .ui-state-active a,
 .ui-state-active a:link,
 .ui-state-active a:visited {
@@ -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


+ 1 - 1
modules/complementary_ration_lib/models/compleRationModel.js

@@ -185,7 +185,7 @@ class CompleRatoinDao {
                     hintsArr = hintsArr.concat(ration.annotation.split('\n'));
                 }
                 ration._doc.hint = hintsArr.join('<br>');
-                ration._doc.hintHeight = hintsArr.length * perHintHeight;//控制定额库悬浮提示位置
+               // ration._doc.hintHeight = hintsArr.length * perHintHeight;//控制定额库悬浮提示位置
             }
             callback(0, rations);
         }

+ 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);
 };

+ 15 - 25
modules/reports/controllers/rpt_tpl_controller.js

@@ -132,6 +132,21 @@ let mExport = {
             }
         });
     },
+    getTplTreeByCompilation: function (req, res) {
+        let params = JSON.parse(req.body.params),
+            compilationId = params.compilationId;
+        // if (req.session.sessionUser && req.session.sessionUser.id) sessionUserId = req.session.sessionUser.id;
+        if (!compilationId) {
+            compilationId = req.session.sessionCompilation._id;
+        }
+        rttFacade.findTplTreeByCompilation(compilationId).then(function(result) {
+            if (result) {
+                callback(req,res,false,"", result);
+            } else {
+                callback(req,res, true,"no result", null);
+            }
+        });
+    },
     updateTreeNodes: function(req, res) {
         let params = JSON.parse(req.body.params),
             nodes = params.nodes;
@@ -268,30 +283,6 @@ let mExport = {
             })
         }
     },
-    // createTplTreeNode: function(req, res){
-    //     let params = JSON.parse(req.body.params),
-    //         lastNodeId = params.lastNodeId,
-    //         nodeData = params.rawNodeData;
-    //     counter.counterDAO.getIDAfterCount(counter.moduleName.report, 1, function(err, result){
-    //         nodeData.ID = result.value.sequence_value;
-    //         let node = new TreeNodeModel(nodeData);
-    //         node.save(function (err, result) {
-    //             if (err) {
-    //                 callback(req,res, "树节点错误!", "", null);
-    //             } else {
-    //                 if (lastNodeId > 0) {
-    //                     TreeNodeModel.update({ID: lastNodeId}, {"NextSiblingID": nodeData.ID}, function(err, rst){
-    //                         if (err) {
-    //                             callback(req,res, "树节点错误!", "", null);
-    //                         } else {
-    //                             callback(req,res, false, "", result);
-    //                         }
-    //                     });
-    //                 } else callback(req,res, false, "", result);
-    //             }
-    //         });
-    //     });
-    // },
     getNewNodeID: function(req, res) {
         let params = JSON.parse(req.body.params),
             scope = params.scope;
@@ -324,7 +315,6 @@ let mExport = {
                     if (err) {
                         callback(req,res, "报表模板创建错误", "", null);
                     } else {
-                        //TreeNodeModel.update();
                         let filter = {"compilationId": compilationId, "engineerId": engineerId, "userId": userId, "items.ID": subNode.ID, "$or": [{"isDeleted": null}, {"isDeleted": false}]};
                         let updateStatement = {$set: {"items.$": subNode}};
                         rttFacade.updateTreeInDetail(filter, updateStatement).then(function (rst) {

+ 13 - 7
modules/reports/facade/rpt_tpl_tree_node_facade.js

@@ -65,15 +65,21 @@ async function findTplTreeByOid(objectId) {
     } else return null;
 }
 
+async function findTplTreeByCompilation(compilationId) {
+    let filter = {"compilationId": compilationId, "$or": [{"isDeleted": null}, {"isDeleted": false}]};
+    return await rpt_tpl_tree_mdl.find(filter);
+}
+
 
 let expObj = {
-    createNewTree:          createNewTree,
-    updateTree:             updateTree,
-    updateTreeInDetail:     updateTreeInDetail,
-    removeTree:             removeTree,
-    removeTreePhycically:   removeTreePhycically,
-    findTplTree:            findTplTree,
-    findTplTreeByOid:       findTplTreeByOid
+    createNewTree:              createNewTree,
+    updateTree:                 updateTree,
+    updateTreeInDetail:         updateTreeInDetail,
+    removeTree:                 removeTree,
+    removeTreePhycically:       removeTreePhycically,
+    findTplTree:                findTplTree,
+    findTplTreeByOid:           findTplTreeByOid,
+    findTplTreeByCompilation:   findTplTreeByCompilation
 };
 
 export {expObj as default};

+ 1 - 0
modules/reports/routes/rpt_tpl_router.js

@@ -21,6 +21,7 @@ module.exports = function (app) {
     rptTplRouter.post('/updateSubLevelOneNode', reportTplController.updateSubLevelOneNode);
     rptTplRouter.post('/removeTreeRootNode', reportTplController.removeTreeRootNode);
     rptTplRouter.post('/getRptTplTree', reportTplController.getRptTplTree);
+    rptTplRouter.post('/getTplTreeByCompilation', reportTplController.getTplTreeByCompilation);
     rptTplRouter.post('/getNewNodeID', reportTplController.getNewNodeID);
     rptTplRouter.post('/updateRptTplNodes', reportTplController.updateTreeNodes);
     rptTplRouter.post('/deleteRptTplNodes', reportTplController.deleteRptTplNodes);

+ 107 - 10
modules/reports/rpt_component/jpc_flow_tab.js

@@ -11,6 +11,8 @@ let JpcCommonOutputHelper = require('./helper/jpc_helper_common_output');
 let JpcAreaHelper = require('./helper/jpc_helper_area');
 let PDFKit = require('pdfkit');
 let fontUtil = require('../util/rpt_font_util');
+let fsUtil = require("../../../public/fsUtil");
+
 
 let JpcFlowTabSrv = function(){};
 JpcFlowTabSrv.prototype.createNew = function(){
@@ -422,10 +424,12 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 JpcBandHelper.setBandArea(bands, rptTpl, pageStatus, !me.isEx, me.isEx);
                 maxRowRec = JpcFlowTabHelper.getMaxRowsPerPage(bands, rptTpl, me.isEx);
             }
+            let handledRowAmt = 0; //handledRowAmt纪录的是真正处理过的显示行数,包含了空白行,主要是为分页用(自动行高)
             function private_addPage(segIdx, grpSeqInfo, isFollow, isMix, mixSplitPoint) {
                 private_resetBandArea();
                 me.pageStatusLst.push(pageStatus.slice(0));
                 currentRecAmt += maxRowRec; //在自动行高的场景下,currentRecAmt在后面还需要调整
+                handledRowAmt += maxRowRec;
                 let redundantRecAmt = 0;
                 pageIdx++;
                 function private_chk_handle_rec_amt(dv, isEx) {
@@ -473,6 +477,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 let grpRecAmt = (grpSeqInfo)?(grpSeqInfo.length*me.group_lines_amt):0;
                 let grpRecAmtEx = 0;
                 let accAutoHeightAmt = 0; //累计的自动行高数量
+                handledRowAmt = 0; //初始化每一段的已处理纪录行数
                 if (followTabEx && followTabEx.group_node_info) {
                     grpRecAmtEx = followTabEx.group_node_info.length * followTabEx.group_lines_amt;
                 }
@@ -574,17 +579,16 @@ JpcFlowTabSrv.prototype.createNew = function(){
                         }
                     } else {
                         //普通流水数据情况
-                        // if ((currentRecAmt + adHocAutoHeightAmt + maxRowRec >= ttlSegRecAmt)
-                        if ((currentRecAmt + accAutoHeightAmt + adHocAutoHeightAmt + maxRowRec >= ttlSegRecAmt)
-                             //&& (adHocAutoHeightAmt < 2 * maxRowRec) ) {
-                             // && ((currentRecAmt + adHocAutoHeightAmt + maxRowRec - ttlSegRecAmt) < maxRowRec) ) {
-                            && ((currentRecAmt + accAutoHeightAmt + adHocAutoHeightAmt + maxRowRec - ttlSegRecAmt) < maxRowRec) ) {
+                        if (handledRowAmt + maxRowRec >= ttlSegRecAmt) {
                             //备注: 理论上自动行高是没有上限的,有可能正常一页的数据可以拓展到3页及以上,在此极端情况下,必须做一些限制判断,否则会出现缺页情况。
+                            // 2018-08-04 其实之前的判断逻辑完全是自找麻烦,而且还不够正确。其实只需要判断已经处理了多少行纪录(所有的都算,包括空白行),
+                            // 与总的seg纪录数想比较,就很容易得到结果,而且能处理更极端的情况。
                             pageStatus[JV.STATUS_SEGMENT_END] = true;
                             pageStatus[JV.STATUS_REPORT_END] = true;
                             private_resetBandArea();
-                            let hasAdHocRow = ((adHocAutoHeightAmt > maxRowRec) ||
-                                              !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, currentRecAmt + adHocAutoHeightAmt, maxRowRec, me.isEx));
+                            let hasAdHocRow = ((adHocAutoHeightAmt > maxRowRec) || !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, handledRowAmt, maxRowRec, me.isEx));
+                                              // !JpcFlowTabHelper.chkSegEnd(bands, rptTpl, ttlSegRecAmt, currentRecAmt + adHocAutoHeightAmt, maxRowRec, me.isEx));
+
                             if (hasAdHocRow) {
                                 //add page info(pre segment end)
                                 pageStatus[JV.STATUS_SEGMENT_END] = false;
@@ -602,7 +606,8 @@ JpcFlowTabSrv.prototype.createNew = function(){
                         }
                     }
                     //检测是否可退出
-                    if ((currentRecAmt + accAutoHeightAmt + adHocAutoHeightAmt >= ttlSegRecAmt) && (pageIdx % me.multiCols === 0)) {
+                    // if ((currentRecAmt + accAutoHeightAmt + adHocAutoHeightAmt >= ttlSegRecAmt) && (pageIdx % me.multiCols === 0)) {
+                    if (handledRowAmt >= ttlSegRecAmt && (pageIdx % me.multiCols === 0)) {
                         //备注:这里必须得考虑多栏的情况,否则会造成pageStatus出界的问题
                         break;
                     }
@@ -737,7 +742,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
         return rst;
     };
     JpcFlowTabResult.outputContent = function(rptTpl, dataObj, page, bands, unitFactor, controls, multiColIdx, $CURRENT_RPT, customizeCfg) {
-        let me = this, rst = [];
+        let me = this, rst = [], prepareObj = {};
         let FLOW_NODE_STR = me.isEx?JV.NODE_FLOW_INFO_EX:JV.NODE_FLOW_INFO;
         let tab = rptTpl[FLOW_NODE_STR][JV.NODE_FLOW_CONTENT];
         let tabEx = (rptTpl[JV.NODE_FLOW_INFO_EX])?rptTpl[JV.NODE_FLOW_INFO_EX][JV.NODE_FLOW_CONTENT]:null;
@@ -790,7 +795,6 @@ JpcFlowTabSrv.prototype.createNew = function(){
                             if (contentValuesIdx[rowIdx][0] !== JV.TYPE_FOLLOW_MODE && contentValuesIdx[rowIdx][1] === JV.DISPLAY_VAL_TYPE_NORMAL) {
                                 rst.push(me.outputTabField(band, tab_field, data_field, contentValuesIdx[rowIdx][2], -1, contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx));
                             } else if (contentValuesIdx[rowIdx][1] === JV.DISPLAY_VAL_TYPE_AUTO_HEIGHT) {
-                                //vIdx.push([followMode, JV.DISPLAY_VAL_TYPE_AUTO_HEIGHT, sortedSequence[startRecIdx + vi]], subValIdx, ttlValAmt);
                                 if (contentValuesIdx[rowIdx][4] === 1) {
                                     //等效于普通输出
                                     rst.push(me.outputTabField(band, tab_field, data_field, contentValuesIdx[rowIdx][2], -1, contentValuesIdx.length, rowIdx, 1, 0, unitFactor, true, controls, multiColIdx));
@@ -805,6 +809,7 @@ JpcFlowTabSrv.prototype.createNew = function(){
                                     } else {
                                         cellItem[JV.PROP_STYLE] = cellItem[JV.PROP_STYLE] + '_AutoHeightMerge_Middle';
                                     }
+                                    prepareAutoHeightCells(prepareObj, cellItem, rst.length - 1, rst);
                                 }
                             }
                         }
@@ -864,6 +869,10 @@ JpcFlowTabSrv.prototype.createNew = function(){
                 }
             }
         }
+        let eliminateCells = combineAutoHeightCells(prepareObj, page, controls);
+        for (let idIdx = eliminateCells.length - 1; idIdx >= 0; idIdx--) {
+            rst.splice(eliminateCells[idIdx], 1);
+        }
         return rst;
     };
     JpcFlowTabResult.outputColumn = function (rptTpl, dataObj, page, segIdx, bands, unitFactor, multiColIdx) {
@@ -1012,4 +1021,92 @@ JpcFlowTabSrv.prototype.createNew = function(){
     return JpcFlowTabResult;
 };
 
+function push_cell(pageCellObj, cell, cellIdx) {
+    let key = cell[JV.PROP_AREA][JV.PROP_LEFT] + '_' + cell[JV.PROP_AREA][JV.PROP_RIGHT];
+    if (!pageCellObj[key]) {
+        pageCellObj[key] = [];
+    }
+    let cellArr = pageCellObj[key];
+    cellArr.push({"cellIdx": cellIdx, "cell": cell});
+}
+
+function prepareAutoHeightCells(prepareObj, cellItem, cellIdx, cellsArr) {
+    if (prepareObj) {
+        if (prepareObj.cellsArr === undefined) {
+            prepareObj.cellsArr = cellsArr;
+            prepareObj.pageCellObj = {};
+        }
+        push_cell(prepareObj.pageCellObj, cellItem, cellIdx);
+    }
+}
+
+function setupControl(mergeCell, controls) {
+    let orgCtrl = null;
+    if ( typeof mergeCell[JV.PROP_CONTROL] === "string") {
+        orgCtrl = controls[mergeCell[JV.PROP_CONTROL]];
+        mergeCell[JV.PROP_CONTROL] = {
+            "Shrink": "T",
+            "ShowZero": orgCtrl.ShowZero,
+            "Horizon": orgCtrl.Horizon,
+            "Vertical": "top",
+            "Wrap": "T",
+            "VerticalForExcel": "justify"
+        };
+    } else {
+        mergeCell[JV.PROP_CONTROL].Shrink = "T";
+        mergeCell[JV.PROP_CONTROL].Vertical = "top";
+        mergeCell[JV.PROP_CONTROL].Wrap = "T";
+        mergeCell[JV.PROP_CONTROL].VerticalForExcel = "justify";
+        orgCtrl = mergeCell[JV.PROP_CONTROL];
+    }
+    return orgCtrl;
+}
+
+function combineAutoHeightCells(prepareObj, page, controls) {
+    let rst = [];
+    if (prepareObj.cellsArr) {
+        //merge cells' value and area
+        //备注: 系统逻辑已经把Cell的顺序放好,无需再做排序。
+        for (let mergeKey in prepareObj.pageCellObj) {
+            if (prepareObj.pageCellObj[mergeKey].length > 1) {
+                let firstMergeCell = prepareObj.pageCellObj[mergeKey][0].cell;
+                firstMergeCell[JV.PROP_STYLE] = firstMergeCell[JV.PROP_STYLE].slice(0, firstMergeCell[JV.PROP_STYLE].indexOf("_AutoHeightMerge"));
+                let orgCtrl = setupControl(firstMergeCell, controls);
+                let validValueAmt = 0;
+                for (let i = 1; i < prepareObj.pageCellObj[mergeKey].length; i++) {
+                    let mergeCell = prepareObj.pageCellObj[mergeKey][i].cell;
+                    if (mergeCell[JV.PROP_STYLE].indexOf("_AutoHeightMerge_Top") < 0) {
+                        //merge into the firstMergeCell!
+                        firstMergeCell[JV.PROP_AREA][JV.PROP_BOTTOM] = mergeCell[JV.PROP_AREA][JV.PROP_BOTTOM];
+                        firstMergeCell[JV.PROP_VALUE] = firstMergeCell[JV.PROP_VALUE] + "|" + mergeCell[JV.PROP_VALUE];
+                        if (mergeCell[JV.PROP_VALUE]) validValueAmt++;
+                        rst.push(prepareObj.pageCellObj[mergeKey][i].cellIdx);
+                        if (i === prepareObj.pageCellObj[mergeKey].length - 1 && validValueAmt === 0) {
+                            firstMergeCell[JV.PROP_CONTROL].Shrink = orgCtrl.Shrink;
+                            firstMergeCell[JV.PROP_CONTROL].Wrap = "F";
+                            firstMergeCell[JV.PROP_CONTROL].VerticalForExcel = null;
+                        }
+                    } else {
+                        if (validValueAmt === 0) {
+                            firstMergeCell[JV.PROP_CONTROL].Shrink = orgCtrl.Shrink;
+                            firstMergeCell[JV.PROP_CONTROL].Wrap = "F";
+                            firstMergeCell[JV.PROP_CONTROL].VerticalForExcel = null;
+                        }
+                        firstMergeCell = prepareObj.pageCellObj[mergeKey][i].cell;
+                        firstMergeCell[JV.PROP_STYLE] = firstMergeCell[JV.PROP_STYLE].slice(0, firstMergeCell[JV.PROP_STYLE].indexOf("_AutoHeightMerge"));
+                        orgCtrl = setupControl(firstMergeCell, controls);
+                        validValueAmt = 0;
+                    }
+                }
+            }
+        }
+        rst.sort(function (i1, i2) {
+            return (i1 > i2)?1:-1;
+        });
+    }
+    // fsUtil.writeObjToFile(prepareObj, "D:/GitHome/ConstructionCost/tmp/afterMergeCellsPrepareObj_" + page + ".jsp");
+    // fsUtil.writeObjToFile(rst, "D:/GitHome/ConstructionCost/tmp/eliminateCells_" + page + ".jsp");
+    return rst;
+}
+
 module.exports = new JpcFlowTabSrv();

+ 5 - 0
modules/reports/util/rpt_excel_util.js

@@ -208,6 +208,9 @@ function writeStyles(stylesObj){
         let textRotation = 0;
         let newHorizontal = excelStyle[JV.CONTROL_PROPS[2]];
         let newVertical = excelStyle[JV.CONTROL_PROPS[3]];
+        if (excelStyle[JV.CONTROL_PROPS[5]]) {
+            newVertical = excelStyle[JV.CONTROL_PROPS[5]];
+        }
         if (parseInt(excelStyle.fontAngle) !== 0) {
             let tmpH = newHorizontal, tmpV = newVertical;
             if (excelStyle.fontAngle > 0) {
@@ -223,6 +226,8 @@ function writeStyles(stylesObj){
                     tmpH = 'right';
                 } else if (newVertical === "bottom") {
                     tmpH = 'left';
+                } else if (newVertical === "justify") {
+                    tmpH = 'justify';
                 } else {
                     tmpH = 'center';
                 }

+ 2 - 5
modules/reports/util/rpt_font_util.js

@@ -6,12 +6,10 @@ let fontMapObj = {
     "宋体": "Smart"
     ,"楷体": "simkai"
     ,"黑体": "simhei"
-    // ,"华文中宋": "STZHONGS"
-    // ,"华文宋体": "STSONG"
-    //"宋体": "Smart"
 };
 //下划线在option中支持
-//另注意:
+//另注意:PDFkit设置字体的时候会检测是否同源,也就是说,如果是同一种字体转换不同的特性(如粗体、斜体),那么在设置的时候会无效
+//      比如前一种是普通的字体,后来想设置这种字体的斜体,实际上这种设置会失效
 
 module.exports = {
     getActualFont: getActualFont
@@ -20,7 +18,6 @@ module.exports = {
 function getActualFont(mapName, isBold, isItalic) {
     let rst = ["Smart"];
     if (fontMapObj[mapName]) rst[0] = fontMapObj[mapName];
-    // rst = rst + (isBold?"_bold":"") + (isItalic?"_italic":"");
     if (isBold) {
         rst.push("_bold");
     }

+ 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",

+ 2 - 1
public/web/rpt_value_define.js

@@ -201,12 +201,13 @@ const JV = {
 
     PAGE_STATUS: ["EveryPage","FirstPage", "LastPage", "SegmentStart", "SegmentEnd", "Group", "CrossRowEnd", "CrossColEnd"],
 
-    CONTROL_PROPS: ["Shrink", "ShowZero", "Horizon", "Vertical", "Wrap"],
+    CONTROL_PROPS: ["Shrink", "ShowZero", "Horizon", "Vertical", "Wrap", "VerticalForExcel"],
     CONTROL_PROP_IDX_SHRINK: 0,
     CONTROL_PROP_IDX_SHOW_ZERO: 1,
     CONTROL_PROP_IDX_HORIZON: 2,
     CONTROL_PROP_IDX_VERTICAL: 3,
     CONTROL_PROP_IDX_WRAP: 4,
+    CONTROL_PROP_IDX_VERTICAL_EXCEL: 5,
     BORDER_STYLE_PROPS: ["LineWeight", "DashStyle", "Color"],
     PROP_LINE_WEIGHT: "LineWeight",
     PROP_DASH_STYLE: "DashStyle",

+ 34 - 5
public/web/sheet/sheet_data_helper.js

@@ -71,6 +71,9 @@ var SheetDataHelper = {
     },
     loadSheetHeader: function (setting, sheet) {
         this.massOperationSheet(sheet, function () {
+            if(setting.rowHeaderWidth !== undefined && setting.rowHeaderWidth !== null){
+                sheet.setColumnWidth(0, setting.rowHeaderWidth, GC.Spread.Sheets.SheetArea.rowHeader);
+            }
             if (setting.frozenCols) {
                 sheet.frozenColumnCount(setting.frozenCols);
             }
@@ -123,7 +126,13 @@ var SheetDataHelper = {
         style.wordWrap = setting.data.wordWrap;
         return style;
     },
+    getTipWidth: function(text){
+        const fontSize = 14.4;
+        let widthArr = text.split('<br>');
+        console.log(widthArr);
+    },
     loadSheetData: function (setting, sheet, datas) {
+        let me = this;
         SheetDataHelper.protectdSheet(sheet);
 
 
@@ -144,9 +153,9 @@ var SheetDataHelper = {
         TipCellType.prototype.processMouseEnter = function (hitinfo) {
             let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
             let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col);
-            let hintHeight = datas[hitinfo.row] ?
+       /*     let hintHeight = datas[hitinfo.row] ?
                                 datas[hitinfo.row].hintHeight ? datas[hitinfo.row].hintHeight : null
-                            : null; //定额库定额悬浮提示位置相关
+                            : null; //定额库定额悬浮提示位置相关*/
             if(tag !== undefined && tag){
                 text = tag;
             }
@@ -154,6 +163,19 @@ var SheetDataHelper = {
                 setting.pos = SheetDataHelper.getObjPos(sheet.getParent().qo);
             }
             if (setting.pos && text && text !== '') {
+                //固定不显示的div,存储文本获取固定div宽度,toolTipElement由于显示和隐藏,获取宽度不正确
+                if(!this._fixedTipElement){
+                    let div = $('#fixedTip')[0];
+                    if (!div) {
+                        div = document.createElement("div");
+                        $(div).css("padding", 5)
+                            .attr("id", 'fixedTip');
+                        $(div).hide();
+                        document.body.insertBefore(div, null);
+                    }
+                    this._fixedTipElement = div;
+                }
+                $(this._fixedTipElement).html(text);
                 if (!this._toolTipElement) {
                     let div = $('#autoTip')[0];
                     if (!div) {
@@ -161,7 +183,7 @@ var SheetDataHelper = {
                         $(div).css("position", "absolute")
                             .css("border", "1px #C0C0C0 solid")
                             .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)")
-                           // .css("font", "9pt Arial")
+                            .css("font", "0.9rem Calibri")
                             .css("background", "white")
                             .css("padding", 5)
                             .attr("id", 'autoTip');
@@ -169,9 +191,16 @@ var SheetDataHelper = {
                         document.body.insertBefore(div, null);
                     }
                     this._toolTipElement = div;
+                    //实时读取位置信息
+                    if(hitinfo.sheet && hitinfo.sheet.getParent().qo){
+                        setting.pos = SheetDataHelper.getObjPos(hitinfo.sheet.getParent().qo);
+                    }
                     $(this._toolTipElement).html(text);
-                    if(hintHeight){
-                        $(this._toolTipElement).css("top", setting.pos.y  + hitinfo.y - hintHeight).css("left", setting.pos.x + hitinfo.x + 15);
+                    //定额库定额特殊处理
+                    if($(hitinfo.sheet.getParent().qo).attr('id') === 'stdSectionRations'){
+                        let divWidth = $(this._fixedTipElement).width(),
+                            divHeight = $(this._fixedTipElement).height();
+                        $(this._toolTipElement).css("top", setting.pos.y  + hitinfo.y - divHeight).css("left", setting.pos.x - divWidth);
                     }
                     else{
                         $(this._toolTipElement).css("top", setting.pos.y + hitinfo.y +15).css("left", setting.pos.x + hitinfo.x + 15);

+ 9 - 3
public/web/tree_sheet/tree_sheet_helper.js

@@ -51,6 +51,9 @@ var TREE_SHEET_HELPER = {
     },
     loadSheetHeader: function (setting, sheet) {
         this.massOperationSheet(sheet, function () {
+            if(setting.rowHeaderWidth !== undefined && setting.rowHeaderWidth !== null){
+                sheet.setColumnWidth(0, setting.rowHeaderWidth, GC.Spread.Sheets.SheetArea.rowHeader);
+            }
             if (setting.frozenCols) {
                 sheet.frozenColumnCount(setting.frozenCols);
             }
@@ -109,6 +112,9 @@ var TREE_SHEET_HELPER = {
     refreshTreeNodeData: function (setting, sheet, nodes, recursive) {
         nodes.forEach(function (node) {
             let iRow = node.serialNo();
+            if(setting.emptyRowHeader){
+                sheet.setValue(iRow, 0, '', GC.Spread.Sheets.SheetArea.rowHeader);
+            }
             if(typeof projectObj !== 'undefined'){
                 let nodeStyle = projectObj.getNodeColorStyle(sheet, node);
                 if(node.data.bgColour){
@@ -477,12 +483,12 @@ var TREE_SHEET_HELPER = {
                 if (!div) {
                     div = document.createElement("div");
                     $(div).css("position", "absolute")
+                    
                         .css("border", "1px #000000 solid")
                         .css("box-shadow", "1px 2px 5px rgba(0,0,0,0)")
-                        .css("font", "9pt Arial")
+                        .css("font", "0.9rem Calibri")
                         .css("background", "#000000")
-                        .css("color",'#FFFFFF')
-                        .css("padding", 5)
+  						.css("color",'#FFFFFF')                        .css("padding", 5)
                         .attr("id", 'autoTip');
                     $(div).hide();
                     document.body.insertBefore(div, null);

+ 8 - 7
test/unit/reports/test_rpt_test_template.js

@@ -31,7 +31,8 @@ let demoPrjId = - 1;
 // let demoRptId = 337; //19表
 // let demoRptId = 361; //封1
 // let demoRptId = 279; //表04
-let demoRptId = 261; //封3
+// let demoRptId = 261; //封3
+let demoRptId = 232; //09
 // let demoRptId = 2260; //测试基本信息
 let pagesize = "A4";
 //288: 11-2表(新)
@@ -42,7 +43,7 @@ let userId_Leng = "5acac1e885bf55000bd055ba"; //小冷User Id2
 // demoPrjId = 720; //QA: DW3
 //demoPrjId = 1626; //QA:
 // demoPrjId = 2260; //QA:
-demoPrjId = 3532; //QA:
+demoPrjId = 3391; //QA:
 //*/
 let userId_Dft = userId_Leng;
 // let userId_Dft = "5a025c4c15074d134c2b9689";
@@ -88,12 +89,12 @@ test('测试 - 测试模板啦: ', function (t) {
                     let pageRst = printCom.outputAsSimpleJSONPageArray(rptTpl, tplData, 1, maxPages, defProperties);
                     if (pageRst) {
                         // fsUtil.writeObjToFile(pageRst, "D:/GitHome/ConstructionCost/tmp/testBuiltPageResult_测试模板.jsp");
-                        // rpt_xl_util.exportExcel(pageRst, pagesize, "local_test_rpt_excel", true, null, null, function(uuidName){
-                        //     console.log("excel uuid: " + uuidName);
-                        // });
-                        rpt_pdf_util.export_pdf_file(pageRst, pagesize, 'local_test_rpt_pdf', function(uuidName){
-                            console.log("pdf uuid: " + uuidName);
+                        rpt_xl_util.exportExcel(pageRst, pagesize, "local_test_rpt_excel", true, null, null, function(uuidName){
+                            console.log("excel uuid: " + uuidName);
                         });
+                        // rpt_pdf_util.export_pdf_file(pageRst, pagesize, 'local_test_rpt_pdf', function(uuidName){
+                        //     console.log("pdf uuid: " + uuidName);
+                        // });
                     } else {
                         console.log("oh! no pages were created!");
                     }

+ 18 - 11
web/building_saas/main/html/main.html

@@ -69,8 +69,8 @@
                     <a href="javascript:void(0)" class="btn btn-sm" title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
                     <a href="javascript:void(0)" class="btn btn-sm" title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>-->
                     <a class="btn btn-light btn-sm" href="javascript:void(0);" aria-expanded="false" data-toggle="modal" data-target="#poj-set"><i class="fa fa-cog" data-toggle="tooltip" data-original-title="项目属性" data-placement="bottom"></i></a>
-                    <span class="btn btn-light btn-sm">
-                        <a class="dropdown-toggle" id="uploadDropDown" href="#" data-toggle="dropdown"><i class="fa fa-cloud-upload" data-toggle="tooltip" data-original-title="导入" data-placement="bottom"></i></a>
+                    <span class="btn btn-light btn-sm" id="importSpan">
+                        <a class="dropdown-toggle" href="#" data-toggle="dropdown"><i class="fa fa-cloud-upload" data-toggle="tooltip" data-original-title="导入" data-placement="bottom"></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>
@@ -83,7 +83,7 @@
                       <a href="javascript:void(0)" class="btn btn-light btn-sm" id="upMove" data-toggle="tooltip" data-placement="bottom" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
                       <a href="javascript:void(0)" class="btn btn-light btn-sm" id="downMove" data-toggle="tooltip" data-placement="bottom" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
                     <span class="btn btn-light btn-sm">
-                      <a href="" data-toggle="dropdown"><span data-toggle="tooltip" data-original-title="显示至" data-placement="bottom"><i class="fa fa-list-ol"></i></span></a>
+                      <a href="" data-toggle="dropdown"><span data-placement="bottom"><i class="fa fa-list-ol"></i></span> 显示至...</a>
                       <div class="dropdown-menu dropdown-menu-left" style="min-width: 6.5rem">
                       <a class="dropdown-item" href="javascript:void(0);" id="displayDXFY">大项费用</a>
                       <a class="dropdown-item" href="javascript:void(0);"  id="displayFB1">一级分部</a>
@@ -95,13 +95,13 @@
                       <a class="dropdown-item" href="javascript:void(0);"  id="displayZD">最底层</a>
                     </div>
                     </span>
-                      <a href="javascript:void(0);" id="ZLFB_btn" class="btn btn-light btn-sm" data-placement="bottom" data-toggle="tooltip" data-original-title="整理分部"><i class="fa fa-retweet" aria-hidden="true"></i></a>
+                      <a href="javascript:void(0);" id="ZLFB_btn" class="btn btn-light btn-sm" data-placement="bottom"><i class="fa fa-retweet" aria-hidden="true"></i> 整理分部</a>
                       <% if (projectData.property.lockBills == true) { %>
-                      <a href="javascript:void(0)"  class="btn btn-light btn-sm" name="lockBills" data-toggle="tooltip" data-placement="bottom" data-original-title="解锁清单"> <i class="fa fa-unlock-alt" aria-hidden="true"></i></a>
+                      <a href="javascript:void(0)"  class="btn btn-light btn-sm" name="lockBills"> <i class="fa fa-unlock-alt" aria-hidden="true"></i> 解锁清单</a>
                       <% } else { %>
-                      <a href="javascript:void(0)"  class="btn btn-light btn-sm" name="lockBills" data-toggle="tooltip" data-placement="bottom" data-original-title="锁定清单"> <i class="fa fa-lock" aria-hidden="true"></i></a>
+                      <a href="javascript:void(0)"  class="btn btn-light btn-sm" name="lockBills"> <i class="fa fa-lock" aria-hidden="true"></i> 锁定清单</a>
                       <% } %>
-                      <a id="switchTznr" href="javascript:void(0);"  class="btn btn-light btn-sm"  data-toggle="tooltip" data-placement="bottom" data-original-title="显示特征"><i class="fa fa-eye" aria-hidden="true"></i></a>
+                      <a id="switchTznr" href="javascript:void(0);"  class="btn btn-light btn-sm"><i class="fa fa-eye" aria-hidden="true"></i> 显示特征</a>
                   </div>
                   <div class="side-tabs">
                       <ul class="nav nav-tabs" role="tablist">
@@ -1363,12 +1363,16 @@
                     <div class="alert alert-success mt-3" id="uploadAlert" role="alert" style="display: none;">
                         广东XXXX项目清单.xlsx 准备导入上传
                     </div>
-                    <button style="margin-top: 2px;" type="button" class="btn btn-primary" id="uploadExample">示例</button>
+                    <!--<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="uploadSheetsHead" style="margin-left: 5px;margin-top: 5px; display: none;" class="input-group form-check"><label class="form-check-label" style="width:270px; overflow: hidden; font-weight: bold">
+                        选择工作表...</label><label style="margin-left: 10px; font-weight: bold">导入到...</label>
+                    </div>
                     <div id="uploadSheets"></div>
                 </div>
                 <div class="modal-footer">
-                    <a href="javascript:void(0);" class="btn btn-primary" id="uploadConfirm">确定导入</a>
+                    <button style="margin-right: 245px;" type="button" class="btn btn-primary" id="uploadExample">示例</button>
+                    <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 +1454,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 +1468,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 +1572,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>

+ 12 - 2
web/building_saas/main/js/models/calc_program.js

@@ -250,6 +250,12 @@ let calcTools = {
             treeNode.changed = true;
         };
 
+        // 不知在何种情况下,tenderUnitFee、tenderTotalFee的值会变成NaN,这里提前处理一下
+        if (isNaN(treeNode.data.feesIndex[feeObj.fieldName].tenderUnitFee))
+            treeNode.data.feesIndex[feeObj.fieldName].tenderUnitFee = undefined;
+        if (isNaN(treeNode.data.feesIndex[feeObj.fieldName].tenderTotalFee))
+            treeNode.data.feesIndex[feeObj.fieldName].tenderTotalFee = undefined;
+
         if (treeNode.data.feesIndex[feeObj.fieldName].tenderUnitFee != feeObj.tenderUnitFee){
             treeNode.data.feesIndex[feeObj.fieldName].tenderUnitFee = feeObj.tenderUnitFee;
             treeNode.changed = true;
@@ -1708,8 +1714,9 @@ class CalcProgram {
                 treeNode.data.programID = null;
                 treeNode.changed = true;
             }
-
-            let f = treeNode.data.feeRate ? treeNode.data.feeRate : 100;
+            let f = 100;
+            if (treeNode.data.feeRate || treeNode.data.feeRate == 0)
+                f = treeNode.data.feeRate;
             let b = treeNode.data.calcBaseValue ? treeNode.data.calcBaseValue : 0;
             let tb = treeNode.data.tenderCalcBaseValue ? treeNode.data.tenderCalcBaseValue : 0;
             let q = nQ ? nQ : 1;
@@ -1992,6 +1999,9 @@ class CalcProgram {
         const totalFeeType = tender ? 'common.tenderTotalFee' : 'common.totalFee';
         function calcNodes(nodes) {
             for (let node of nodes) {
+                if(!node){
+                    continue;
+                }
                 if (!excludeNodes.includes(node)){
                     if (node.source && node.source.children && node.source.children.length > 0) {
                         calcNodes(node.children);

+ 17 - 2
web/building_saas/main/js/models/ration.js

@@ -627,14 +627,28 @@ var Ration = {
             }
             else return null;
         };
-        ration.prototype.addNewRationForPaste = function (rationType) {
+        ration.prototype.addNewRationFast = function (rationType) {
             let me = this;
             let project = projectObj.project, sheetController = projectObj.mainController;
             let engineering = projectInfoObj.projectInfo.property.engineering;
             let selected = project.mainTree.selected, newSource = null, newNode = null,pre=null,br=null;
             let billItemID = null,serialNo=1,nextID=null;
             if (selected === null) { return null; }
-            if (selected.sourceType === project.Ration.getSourceType()) {
+            if (selected.sourceType === project.Bills.getSourceType() && selected.depth() > 0) {
+                if(selected.data.type === billType.FB){
+                    return null;
+                }
+                else if (selected.source.children.length > 0) {
+                    alert('当前清单已有清单子项,不能套用定额。');
+                } else if (selected.data.calcBase&&selected.data.calcBase!="") {
+                    alert('当前有基数计算不能插入定额/量价/工料机。');
+                } else {
+                    billItemID = selected.source.getID();
+                    nextID = selected.tree.rootID();
+                    br = this.getBillsSortRation(billItemID);
+                    serialNo = br.length > 0 ? br[br.length - 1].serialNo + 1 : 1
+                }
+            } else if (selected.sourceType === project.Ration.getSourceType()) {
                 billItemID = selected.getParentID();
                 br = this.getBillsSortRation(billItemID);
                 serialNo = selected.data.serialNo+1;
@@ -669,6 +683,7 @@ var Ration = {
                     newNode.data = newSource;
                 })
                 ProjectController.syncDisplayNewNode(sheetController, newNode);
+                $.bootstrapLoading.end();
                 return newNode;
             }
             else return null;

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

@@ -0,0 +1,482 @@
+'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'),
+            sheetHeader = $('#uploadSheetsHead');
+        $('#uploadSheets').height('');
+        sheetArea.empty();
+        for(let sheetName of sheetNames){
+            let sheetDiv = $(`<div style="margin-left: 5px;margin-top: 5px;" title="${sheetName}" 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();
+            sheetHeader.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}
+})();

+ 10 - 8
web/building_saas/main/js/views/main_tree_col.js

@@ -54,8 +54,8 @@ let MainTreeCol = {
             else
                 rst = isDef(node.data.code) ? node.data.code : '';
 
-            if (node.data.adjustState && rst != '')
-                rst = rst + rationPrefix.replace;
+/*            if (node.data.adjustState && rst != '')
+                rst = rst + rationPrefix.replace;*/
             return rst;
         },
         marketPrice:function (node) {
@@ -521,21 +521,23 @@ function switchTznrHtml(show) {
         return;
     }
     if(show){
-        $('#switchTznr').attr('data-original-title', '显示特征');
+        /*$('#switchTznr').attr('data-original-title', '显示特征');
         $('#switchTznr').find('i').removeClass('fa-eye-slash');
-        $('#switchTznr').find('i').addClass('fa-eye');
+        $('#switchTznr').find('i').addClass('fa-eye');*/
+        $('#switchTznr').html('<i class="fa fa-eye" aria-hidden="true"></i> 显示特征');
     }
     else {
-        $('#switchTznr').attr('data-original-title', '隐藏特征');
+       /* $('#switchTznr').attr('data-original-title', '隐藏特征');
         $('#switchTznr').find('i').removeClass('fa-eye');
-        $('#switchTznr').find('i').addClass('fa-eye-slash');
+        $('#switchTznr').find('i').addClass('fa-eye-slash');*/
+        $('#switchTznr').html('<i class="fa fa-eye-slash" aria-hidden="true"></i> 隐藏特征');
     }
 }
 
 $('#switchTznr').click(function () {
     let me = colSettingObj;
-    let cur = $(this).attr('data-original-title');
-    //$("[data-toggle='tooltip']").tooltip('hide');
+    //let cur = $(this).attr('data-original-title');
+    let cur = $(this).text();
     if(cur.includes('显示特征')){
         switchTznrHtml(false);
         me.setVisible('itemCharacterText', true);

+ 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);
 
         }

+ 77 - 117
web/building_saas/main/js/views/project_view.js

@@ -531,7 +531,7 @@ var projectObj = {
             if (isDef(node.data.prefix) && node.data.prefix !== rationPrefix.none){
                  value = value.replace(new RegExp(node.data.prefix), '');
             };
-            value = value.replace(new RegExp(rationPrefix.replace), '');
+            // value = value.replace(new RegExp(rationPrefix.replace), '');
             info.sheet.setValue(info.row, info.col, value);
         }
         if(node&&fieldName =='quantity'&&(node.data.quantityEXP !==null||node.data.quantityEXP !==undefined)){
@@ -645,7 +645,7 @@ var projectObj = {
                     for (let i = 0; i < add; i++) {
                         switch (nodeType) {
                             case 1:
-                                curNode = projectObj.project.Ration.addNewRationForPaste(rationType.ration);
+                                curNode = projectObj.project.Ration.addNewRationFast(rationType.ration);
                                 break;
                             case 2:
                                 curNode = ProjectController.addFB(projectObj.project, projectObj.mainController);
@@ -681,7 +681,7 @@ var projectObj = {
                         // 编号去掉“换、借、补”等多余的字
                         code = code.replace(new RegExp(rationPrefix.complementary), '');
                         code = code.replace(new RegExp(rationPrefix.borrow), '');
-                        code = code.replace(new RegExp(rationPrefix.replace), '');
+                        // code = code.replace(new RegExp(rationPrefix.replace), '');
                         updateRationCodes.push({'node':ptNode, value:code});
                     }
                     projectObj.project.Ration.updateRationCodes(updateRationCodes);
@@ -1741,11 +1741,11 @@ var projectObj = {
     },
     loadLockBillsButton:function () {
         if(projectInfoObj.projectInfo.property.lockBills == true){
-            $("a[name='lockBills']").attr("data-original-title","解锁清单");
-            $("a[name='lockBills']").html('<i class="fa fa-unlock-alt" aria-hidden="true"></i>');
+            //$("a[name='lockBills']").attr("data-original-title","解锁清单");
+            $("a[name='lockBills']").html('<i class="fa fa-unlock-alt" aria-hidden="true"></i> 解锁清单');
         }else {
-            $("a[name='lockBills']").attr("data-original-title","锁定清单");
-            $("a[name='lockBills']").html('<i class="fa fa-lock" aria-hidden="true"></i>');
+            //$("a[name='lockBills']").attr("data-original-title","锁定清单");
+            $("a[name='lockBills']").html('<i class="fa fa-lock" aria-hidden="true"></i> 锁定清单');
         }
     },
     editContent:function (node,fieldID) {//右键编辑工程内容、服务内容、签证及索赔依据
@@ -1921,7 +1921,9 @@ $('#downLevel').click(function () {
     }
 });
 $('#insertRation').click(function () {
-    projectObj.project.Ration.addNewRation(null,rationType.ration);
+    // projectObj.project.Ration.addNewRation(null,rationType.ration);
+    // 连续点工具栏的插入定额按钮,显示树结构画线有问题。
+    projectObj.project.Ration.addNewRationFast(rationType.ration);
 });
 $('#upMove').click(function () {
     var controller = projectObj.mainController, project = projectObj.project;
@@ -2457,128 +2459,84 @@ $('#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;
+//选择导入的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;
+       }
+       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,
@@ -2586,12 +2544,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");
                     //更新前端
@@ -2614,11 +2569,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');
@@ -2709,6 +2667,7 @@ function doAfterImport(resData){
         }
         //如果清单未锁定,导入后锁定清单
         if(!projectInfoObj.projectInfo.property.lockBills){
+            console.log('enterLockBillsClick');
             $("a[name='lockBills']").click();
         }
         $.bootstrapLoading.end();
@@ -2737,6 +2696,7 @@ $(function () {
         //恢复选择表高度
         $('#uploadSheets').height('');
         $('#uploadSheets').hide();
+        $('#uploadSheetsHead').hide();
     });
 
    /* $("#billsSpread").mouseover(function(){
@@ -2784,7 +2744,7 @@ function disableTools(){
     $('#downMove').remove();
     $('#ZLFB_btn').remove();
     $('#switchTznr').remove();
-    $('#uploadDropDown').remove();
+    $('#importSpan').remove();
     $('a[name="lockBills"]').remove();
     //关于计算
     $('#poj-settings-4').find('input').prop('disabled', 'disabled');
@@ -2803,7 +2763,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');

+ 2 - 0
web/building_saas/main/js/views/std_billsGuidance_lib.js

@@ -22,6 +22,8 @@ const billsGuidance = (function () {
         tree: null,
         controller: null,
         treeSetting: {
+            emptyRowHeader: true,
+            rowHeaderWidth: 15,
             treeCol: 0,
             emptyRows: 0,
             headRows: 1,

+ 4 - 2
web/building_saas/main/js/views/std_bills_lib.js

@@ -330,6 +330,8 @@ var billsLibObj = {
         });
     },
     stdBillsTreeSetting: {
+        "emptyRowHeader": true,
+        "rowHeaderWidth": 15,
         "treeCol": 0,
         "emptyRows":0,
         "headRows":1,
@@ -338,7 +340,7 @@ var billsLibObj = {
         ],
         "defaultRowHeight": 21,
         "cols":[{
-            "width":160,
+            "width":140,
             "readOnly": true,
             "head":{
                 "titleNames":["项目编码"],
@@ -355,7 +357,7 @@ var billsLibObj = {
                 "font":"Arial"
             }
         }, {
-            "width":220,
+            "width":190,
             "readOnly": true,
             "head":{
                 "titleNames":["项目名称"],

+ 5 - 4
web/building_saas/main/js/views/std_ration_lib.js

@@ -116,10 +116,10 @@ var rationLibObj = {
         sheet.suspendPaint();
         sheet.suspendEvent();
         for(let i = 0, len = sheet.getRowCount(); i < len; i++){
-            sheet.setTag(i, 1, '');
+            sheet.setTag(i, 0, '');
         }
         for(let i = 0, len = datas.length; i < len; i++){
-            sheet.setTag(i, 1, datas[i].hint ? datas[i].hint : '');
+            sheet.setTag(i, 0, datas[i].hint ? datas[i].hint : '');
         }
         sheet.resumePaint();
         sheet.resumeEvent();
@@ -189,6 +189,8 @@ var rationLibObj = {
         });
     },
     rationChapterTreeSetting: {
+        "emptyRowHeader": true,
+        "rowHeaderWidth": 15,
         "emptyRows":0,
         "headRows":1,
         "headRowHeight":[30],
@@ -221,6 +223,7 @@ var rationLibObj = {
         "cols":[{
             "width":60,
             "readOnly": true,
+            "showHint": true,
             "head":{
                 "titleNames":["编码"],
                 "spanCols":[1],
@@ -238,7 +241,6 @@ var rationLibObj = {
         }, {
             "width":220,
             "readOnly": true,
-            "showHint": true,
             "head":{
                 "titleNames":["名称"],
                 "spanCols":[1],
@@ -318,7 +320,6 @@ var rationLibObj = {
         }
         return parseInt(projectInfoObj.projectInfo.engineeringInfo.ration_lib[0].id);
     }
-
 };
 
 addEventOnResize(rationLibObj.refreshSettingForHint);

+ 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>

+ 9 - 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;
     }
@@ -2846,6 +2846,12 @@ function setDataToSideBar() {
 
 //获取建设项目的全部单位工程
 function getProjTenders(proj) {
+    if(proj.data.projType === projectType.tender){
+        proj = proj.parent.parent;
+    }
+    else if(proj.data.projType === projectType.engineering){
+        proj = proj.parent;
+    }
     let rst = [];
     let engineerings = proj.children || null;
     if (engineerings) {

+ 14 - 9
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>
@@ -30,7 +35,7 @@
             </li>
             <% if (action === 'index' && controller === 'main') {%>
             <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" data-toggle="tooltip" data-placement="bottom" data-original-title="工具"></i></a>
+                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-wrench" data-placement="bottom"></i> 工具</a>
                 <div class="dropdown-menu dropdown-menu-right">
                     <a id="compleRationLib" class="dropdown-item" href="javascript:void(0);" data-toggle="modal" data-target="#comple-ration">定额库编辑器</a>
                     <a id="compleGljLib" class="dropdown-item" href="/complementaryGlj" target="_">人材机库编辑器</a>
@@ -38,7 +43,7 @@
             </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-question-circle-o" data-toggle="tooltip" data-placement="bottom" data-original-title="帮助"></i></a>
+                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-question-circle-o" data-placement="bottom"></i> 帮助</a>
                 <div class="dropdown-menu dropdown-menu-right">
                     <a class="dropdown-item" href="#">帮助</a>
                     <a class="dropdown-item" href="#">升级说明</a>