Browse Source

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

MaiXinRong 4 năm trước cách đây
mục cha
commit
19be94fdb8

+ 44 - 0
app/controller/material_controller.js

@@ -990,6 +990,50 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        /**
+         * 批量下载 - 压缩成zip文件返回
+         * @param {Object} ctx - 全局上下文
+         */
+        async downloadZip(ctx) {
+            try {
+                const fileIds = JSON.parse(ctx.request.query.fileIds);
+                // const { fileIds } = JSON.parse(ctx.request.body.data);
+                // console.log('fileIds', fileIds);
+                const zipFilename = `${ctx.tender.data.name}-材料调差-${ctx.params.order}-附件.zip`;
+                const time = Date.now();
+                const zipPath = `app/public/upload/${ctx.tender.id}/tc/fu_jian_zip${time}.zip`;
+                const size = await ctx.service.stageAtt.compressedFile(fileIds, zipPath);
+
+                // 解决中文无法下载问题
+                const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
+                let disposition = '';
+                if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
+                    disposition = 'attachment; filename=' + encodeURIComponent(zipFilename);
+                } else if (userAgent.indexOf('firefox') >= 0) {
+                    disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(zipFilename) + '"';
+                } else {
+                    /* safari等其他非主流浏览器只能自求多福了 */
+                    disposition = 'attachment; filename=' + new Buffer(zipFilename).toString('binary');
+                }
+                ctx.response.set({
+                    'Content-Type': 'application/octet-stream',
+                    'Content-Disposition': disposition,
+                    'Content-Length': size,
+                });
+                const readStream = fs.createReadStream(path.join(this.app.baseDir, zipPath));
+
+                ctx.body = readStream;
+                // ctx.body = fs.readFileSync(path.resolve(this.app.baseDir, zipPath));
+                readStream.on('close', () => {
+                    if (fs.existsSync(path.resolve(this.app.baseDir, zipPath))) {
+                        fs.unlinkSync(path.resolve(this.app.baseDir, zipPath));
+                    }
+                });
+            } catch (error) {
+                this.log(error);
+            }
+        }
     }
 
     return MaterialController;

+ 2 - 2
app/controller/report_controller.js

@@ -436,7 +436,7 @@ module.exports = app => {
             // const roleRel = (params.stage_status === 3) ? (await ctx.service.roleRptRel.getRoleRptRelByDetailIds(params.tender_id, params.rpt_tpl_id)) : [];
             const pageRstArr = await getMultiRptsCommon(ctx, params, JV.OUTPUT_TYPE_NORMAL, this.app.baseDir);
             // console.log('params.stage_status: ' + params.stage_status);
-            fsUtil.writeObjToFile(pageRstArr, 'D:/GitHome/temp/testBuiltPageResult.js');
+            // fsUtil.writeObjToFile(pageRstArr, 'D:/GitHome/temp/testBuiltPageResult.js');
             for (const pageRst of pageRstArr) {
                 for (const page of pageRst.items) {
                     page[JV.PROP_WATERMARK_CELLS] = [];
@@ -1089,7 +1089,7 @@ function fillWaterMark(pageRstArray) {
         const top = Math.round(area[JV.PROP_TOP] + h / 2 - orgWaterMarkHeight / 2);
         const bottom = top + orgWaterMarkHeight;
         return {
-            signature_name: JV.SIGNATURE_NAME_DUMMY,
+            signature_name: JV.SIGNATURE_NAME_DUMMY_WATER_MARK,
             path: '/public/images/not_Approve.png',
             sign_path: '/public/images/not_Approve.png',
             pic: null,

+ 60 - 12
app/public/js/material_file.js

@@ -46,9 +46,9 @@ $(document).ready(function () {
         }
     })
     // 选择/未选所有期列表
-    $('#file-checkbox').click(function() {
-        getAllList()
-    })
+    // $('#file-checkbox').click(function() {
+    //     getAllList()
+    // })
 
     // 删除附件
     $('body').on('click', '.delete-file', function () {
@@ -100,6 +100,20 @@ $(document).ready(function () {
 
             curPageNo !== $(this).text() && getAllList(parseInt($(this).text()))
         }
+    });
+
+    $('.dropdown-item').click(function() {
+        const type = $('#dropdownMenuButton').attr('btn-type')
+        if (type === 'curr') {
+            $(this).text('当前期')
+            $('#dropdownMenuButton').text('所有期')
+            $('#dropdownMenuButton').attr('btn-type', 'all')
+        } else {
+            $(this).text('所有期')
+            $('#dropdownMenuButton').text('当前期')
+            $('#dropdownMenuButton').attr('btn-type', 'curr')
+        }
+        getAllList()
     })
     // 生成所有附件列表
     function getAllList(currPageNum = 1) {
@@ -109,14 +123,14 @@ $(document).ready(function () {
         // 总页数
         const pageNum = Math.ceil(total/pageCount);
         // 当前页附件内容
-        const currPageAttData = fileData && $('#file-checkbox').is(':checked') ? fileData.slice((currPageNum-1)*pageCount, currPageNum*pageCount) : filterFileData.map((v, index) => {
+        const currPageAttData = fileData && $('#dropdownMenuButton').attr('type') === 'all' ? fileData.slice((currPageNum-1)*pageCount, currPageNum*pageCount) : filterFileData.map((v, index) => {
             return {...v, index }
         }).slice((currPageNum-1)*pageCount, currPageNum*pageCount);
 
         renderHtml(currPageAttData)
         // 渲染分页器
         renderPagination(currPageNum, pageNum)
-    }
+    };
 
 
     function renderPagination(pageNo, pageSize) {
@@ -185,12 +199,14 @@ $(document).ready(function () {
         }
 
         $('.page-next').before(html)
-    }
+    };
 
     function renderHtml(list) {
         let html = '';
+        $('#check-all-file').prop("checked", false)
         list.forEach(fileInfo => {
             html += `<tr style="height: 31px;">
+            <td width="25"><input type="checkbox" class="check-file" file-id=${fileInfo.id}></td>
             <td>${fileInfo.index + 1}</td>
             <td><a href="/${fileInfo.filepath}" target="_blank">${fileInfo.file_name}</a></td>
             <td>${fileInfo.file_size}</td>
@@ -198,18 +214,50 @@ $(document).ready(function () {
             <td>${fileInfo.upload_time}</td>`
             if (fileInfo.canDel ) {
                 html += `<td>
-                <a class="btn btn-light btn-sm delete-file" data-attid="${fileInfo.id}" title="删除附件">
+                <a href="/${fileInfo.filepath}" class="btn btn-light btn-sm" title="下载"><span class="fa fa-download text-primary"></span></a>
+                <a href="javascript:void(0);" class="btn btn-light btn-sm delete-file" data-attid="${fileInfo.id}" title="删除附件">
                 <span class="fa fa-trash text-danger"></span>
                 </a>
                 </td></tr>`
             } else {
-                html += `<td></td></tr>`
+                html += `<td><a href="/${fileInfo.filepath}" class="btn btn-light btn-sm" title="下载"><span class="fa fa-download text-primary"></span></a></td></tr>`
             }
         })
         $('#file-list').empty();
         $('#file-list').append(html);
-    }
+    };
+
+    $('#file-list').on('click', '.check-file', function() {
+        const checkedList = $('#file-list').find('input:checked')
+        const childs = $('#file-list').children().length
+        const checkBox = $('#check-all-file')
+        if (checkedList.length === childs) {
+            checkBox.prop("checked", true)
+        } else {
+            checkBox.prop("checked", false)
+        }
+    })
+    $('#check-all-file').click(function() {
+        const isCheck = $(this).is(':checked')
+        $('#file-list').children().each(function() {
+            $(this).find('input:checkbox').prop("checked", isCheck)
+        })
+    });
 
+    $('#bach-download').click(function() {
+        const fileIds = []
+        $( '#file-list .check-file:checked').each(function() {
+            const fileId = $(this).attr('file-id')
+            fileId && fileIds.push(fileId)
+        })
+        console.log('fileIds', fileIds)
+
+        if (fileIds.length) {
+            // postData( `/tender/${tid}/measure/material/${order}/file/download/compresse-file`, { fileIds })
+            $('#downloadZip').attr('href', `/tender/${tid}/measure/material/${order}/file/download/compresse-file?fileIds=${JSON.stringify(fileIds)}`);
+            $('#downloadZip')[0].click();
+        }
+    });
     function handleFileList(fileList) {
         fileData = fileList.map((file, index) => {
             let showDel = false
@@ -224,7 +272,7 @@ $(document).ready(function () {
             }
             return showDel ? {...file, canDel: true, index} : {...file, index}
         })
-    }
+    };
 
     function calcCount() {
         // 附件总数
@@ -233,7 +281,7 @@ $(document).ready(function () {
             total = fileData && fileData.filter(file => file.mid === parseInt(mid) && file.tid === parseInt(tid)).length
         }
         return total
-    }
+    };
 
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
@@ -273,4 +321,4 @@ function validateFiles(files) {
         }
         return true
     })
-}
+};

+ 5 - 1
app/reports/rpt_component/helper/jpc_helper_discrete.js

@@ -96,7 +96,11 @@ const JpcDiscreteHelper = {
                                         const signatureItem = { signature_name: JV.SIGNATURE_NAME_DUMMY, path: item[JV.PROP_VALUE], pic: null }; // 这里的pic数据在指标中里已经处理过了
                                         signatureItem[JV.PROP_CONTROL] = item[[JV.PROP_CONTROL]];
                                         signatureItem[JV.PROP_STYLE] = item[[JV.PROP_STYLE]];
-                                        signatureItem[JV.PROP_AREA] = item[JV.PROP_AREA];
+                                        signatureItem[JV.PROP_AREA] = {};
+                                        signatureItem[JV.PROP_AREA][JV.PROP_LEFT] = item[JV.PROP_AREA][JV.PROP_LEFT];
+                                        signatureItem[JV.PROP_AREA][JV.PROP_RIGHT] = item[JV.PROP_AREA][JV.PROP_RIGHT];
+                                        signatureItem[JV.PROP_AREA][JV.PROP_TOP] = item[JV.PROP_AREA][JV.PROP_TOP];
+                                        signatureItem[JV.PROP_AREA][JV.PROP_BOTTOM] = item[JV.PROP_AREA][JV.PROP_BOTTOM];
                                         signatureRst.push(signatureItem);
                                         item[JV.PROP_VALUE] = '';
                                     }

+ 8 - 4
app/reports/rpt_component/jpc_flow_tab.js

@@ -538,8 +538,10 @@ JpcFlowTabSrv.prototype.createNew = function() {
                 maxRowRec = JpcFlowTabHelper.getMaxRowsPerPage(bands, rptTpl, me.isEx);
                 me.setupAutoHeightData(bands, segIdx, rptTpl, dataObj, $CURRENT_RPT, defProperties, outputType);
                 let adHocAutoHeightAmt = 0;
-                for (let loop = 0; loop < me.auto_height_info[segIdx].length; loop++) {
-                    adHocAutoHeightAmt += (me.auto_height_info[segIdx][loop] - 1);
+                if (me.auto_height_info.length > 0) {
+                    for (let loop = 0; loop < me.auto_height_info[segIdx].length; loop++) {
+                        adHocAutoHeightAmt += (me.auto_height_info[segIdx][loop] - 1);
+                    }
                 }
                 me.pageStatusLst.push(pageStatus.slice(0));
                 pageIdx++;
@@ -819,8 +821,10 @@ JpcFlowTabSrv.prototype.createNew = function() {
             const tab = rptTpl[JV.NODE_FLOW_INFO][JV.NODE_FLOW_CONTENT];
             const flowContentBand = bands[tab[JV.PROP_BAND_NAME]];
             let adHocAutoHeightAmt = 0;
-            for (let loop = 0; loop < me.auto_height_info[segIdx].length; loop++) {
-                adHocAutoHeightAmt += (me.auto_height_info[segIdx][loop] - 1);
+            if (me.auto_height_info.length > 0) {
+                for (let loop = 0; loop < me.auto_height_info[segIdx].length; loop++) {
+                    adHocAutoHeightAmt += (me.auto_height_info[segIdx][loop] - 1);
+                }
             }
             const rowAmt = Math.max(maxRowRec, me.segments[segIdx].length + adHocAutoHeightAmt); // 如不满一页,则需要填充满一页
             const actH = JpcFlowTabHelper.getActualContentAreaHeight(bands, rptTpl, me.segments, rowAmt, page, me.isEx);

+ 1 - 0
app/reports/rpt_component/jpc_value_define.js

@@ -227,6 +227,7 @@ module.exports = {
     RUN_TYPE_BEFORE_GROUP_TEXT_OUT: 'before_group_text_output',
 
     SIGNATURE_NAME_DUMMY: 'dummy_pic',
+    SIGNATURE_NAME_DUMMY_WATER_MARK: 'dummy_pic_warter_mark',
 
     PAGE_STATUS: ['EveryPage', 'FirstPage', 'LastPage', 'SegmentStart', 'SegmentEnd', 'Group', 'CrossRowEnd', 'CrossColEnd'],
 

+ 71 - 57
app/reports/util/rpt_excel_util.js

@@ -1059,10 +1059,14 @@ function writeDrawings(pageData, signKeyArr, signPathArr, isSinglePage, signShee
     const rst = [];
     // console.log('isSinglePage: ' + isSinglePage);
     if (isSinglePage) {
+        // console.log('singlePage! ');
         rst.push(writeDrawing(pageData, null, signKeyArr[0]));
     } else {
+        // console.log('pageData! ');
         for (let i = 0; i < pageData.items.length; i++) {
             if (signSheetIdxArr[i]) {
+                // console.log('signKeyArr[' + i + ']');
+                // console.log(signKeyArr[i]);
                 rst.push(writeDrawing(pageData, pageData.items[i], signKeyArr[i]));
             }
         }
@@ -1165,7 +1169,10 @@ function writeDrawing(pageData, sheetData, subSignKeyArr) {
     preAnalyzePos(pageData, sheetData, xPos, yPos, yMultiPos);
     rst.push('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>');
     rst.push('<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">');
+    // console.log('subSignKeyArr');
+    // console.log(subSignKeyArr);
     if (sheetData) {
+        // console.log('sheetData');
         let startPicIdx = 2;
         for (const sCell of sheetData[JV.PROP_SIGNATURE_CELLS]) {
             if (subSignKeyArr.indexOf(sCell.signature_name) >= 0) {
@@ -1180,6 +1187,8 @@ function writeDrawing(pageData, sheetData, subSignKeyArr) {
             }
         }
     } else {
+        // console.log('pageData.items[0][JV.PROP_SIGNATURE_CELLS]');
+        // console.log(pageData.items[1][JV.PROP_SIGNATURE_CELLS]);
         // total data in one sheet
         let rowOffset = 0;
         for (let i = 0; i < pageData.items.length; i++) {
@@ -1190,6 +1199,11 @@ function writeDrawing(pageData, sheetData, subSignKeyArr) {
                 if (subSignKeyArr.indexOf(sCell.signature_name) >= 0) {
                     private_setSheetDrawingCellData(sCell, tmpPos, startPicIdx, rowOffset);
                     startPicIdx++;
+                // } else {
+                //     console.log('index < 0 subSignKeyArr: ');
+                //     console.log(subSignKeyArr);
+                //     console.log('sCell.signature_name');
+                //     console.log(sCell.signature_name);
                 }
             }
             for (const sCell of shtItemData[JV.PROP_WATERMARK_CELLS]) {
@@ -1283,7 +1297,7 @@ function createDummySignatureCell(pageData) {
     }
 }
 
-function _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel, signSheetIdxArr) {
+function _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel, signSheetIdxArr, isSinglePage) {
     // 备注:电子签名是以图形的方式处理,一页可以有多个签名,多页的签名基本是引用同样的图片,在这里先处理一下,后期统一引用。
     //      另:以后的图片(在电子签名(signature_cells)以外的图片)会单独处理(如计算草图、水印等)
     let rst = false;
@@ -1298,8 +1312,16 @@ function _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel,
     // for (const page of pageData.items) {
     for (let pageIdx = 0; pageIdx < pageData.items.length; pageIdx++) {
         const page = pageData.items[pageIdx];
-        signKeyArr.push([]);
-        signPathArr.push([]);
+        if (isSinglePage) {
+            if (pageIdx === 0) {
+                signKeyArr.push([]);
+                signPathArr.push([]);
+            }
+        } else {
+            signKeyArr.push([]);
+            signPathArr.push([]);
+        }
+        const curSignIdx = signKeyArr.length - 1;
         signSheetIdxArr[pageIdx] = false;
         // console.log('page index: ' + pageIdx);
         // console.log(page[JV.PROP_SIGNATURE_CELLS]);
@@ -1313,12 +1335,12 @@ function _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel,
                 //     rst = true;
                 //     signSheetIdxArr[pageIdx] = true;
                 // }
-                if (signKeyArr[pageIdx].indexOf(signature.signature_name) < 0) {
+                if (signKeyArr[curSignIdx].indexOf(signature.signature_name) < 0) {
                     if (signature.pic) {
                         const signPath = { path: null, pic: null };
-                        signPathArr[pageIdx].push(signPath);
+                        signPathArr[curSignIdx].push(signPath);
                         signPath.pic = signature.pic; // 历史报表
-                        signKeyArr[pageIdx].push(signature.signature_name);
+                        signKeyArr[curSignIdx].push(signature.signature_name);
                         rst = true;
                         signSheetIdxArr[pageIdx] = true;
                     } else {
@@ -1327,16 +1349,16 @@ function _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel,
                                 // console.log('signature.signature_name: ' + signature.signature_name);
                                 if (role.sign_pic) {
                                     const signPath = { path: null, pic: null };
-                                    signPathArr[pageIdx].push(signPath);
+                                    signPathArr[curSignIdx].push(signPath);
                                     signPath.pic = role.sign_pic;
-                                    signKeyArr[pageIdx].push(signature.signature_name);
+                                    signKeyArr[curSignIdx].push(signature.signature_name);
                                     rst = true;
                                     signSheetIdxArr[pageIdx] = true;
                                 } else if (role.sign_path) {
                                     const signPath = { path: null, pic: null };
-                                    signPathArr[pageIdx].push(signPath);
+                                    signPathArr[curSignIdx].push(signPath);
                                     signPath.path = role.sign_path;
-                                    signKeyArr[pageIdx].push(signature.signature_name);
+                                    signKeyArr[curSignIdx].push(signature.signature_name);
                                     rst = true;
                                     signSheetIdxArr[pageIdx] = true;
                                 }
@@ -1350,15 +1372,17 @@ function _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, roleRel,
         if (page[JV.PROP_WATERMARK_CELLS] && page[JV.PROP_WATERMARK_CELLS].length > 0) {
             // 水印,单独处理
             const waterMarkCell = page[JV.PROP_WATERMARK_CELLS][0];
-            // console.log('waterMarkCell');
-            // console.log(waterMarkCell);
-            const signPath = { path: null, pic: null };
-            signPathArr[pageIdx].push(signPath);
-            signPath.pic = waterMarkCell.pic;
-            signPath.path = waterMarkCell.path;
-            signKeyArr[pageIdx].push(waterMarkCell.signature_name);
-            rst = true;
-            signSheetIdxArr[pageIdx] = true;
+            if (signKeyArr[curSignIdx].indexOf(waterMarkCell.signature_name) < 0) {
+                // console.log('waterMarkCell');
+                // console.log(waterMarkCell);
+                const signPath = { path: null, pic: null };
+                signPathArr[curSignIdx].push(signPath);
+                signPath.pic = waterMarkCell.pic;
+                signPath.path = waterMarkCell.path;
+                signKeyArr[curSignIdx].push(waterMarkCell.signature_name);
+                rst = true;
+                signSheetIdxArr[pageIdx] = true;
+            }
         }
     }
     return rst;
@@ -1393,7 +1417,7 @@ module.exports = {
         // console.log(thisRoleRel);
 
         // const hasSignature = false; // 暂时不支持电子签名、草图导出excel
-        const hasSignature = _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, thisRoleRel, signSheetIdxArr); // 因草图的关系,thisRoleRel是否为null就不是充要的条件
+        const hasSignature = _checkAndSetSignatureCache(pageData, signKeyArr, signPathArr, thisRoleRel, signSheetIdxArr, isSinglePage); // 因草图的关系,thisRoleRel是否为null就不是充要的条件
         // console.log('signKeyArr');
         // console.log(signKeyArr);
         // console.log('signPathArr');
@@ -1473,6 +1497,7 @@ module.exports = {
             // 5.2
             const zip_drawings = zip_xl.folder('drawings');
             data = writeDrawings(pageData, signKeyArr, signPathArr, isSinglePage, signSheetIdxArr);
+            // data = writeDrawings(pageData, signKeyArr, signPathArr, false, signSheetIdxArr); // 备注:这里根本不需要考虑是否single page
             // console.log('isSinglePage: ' + isSinglePage);
             // console.log(data);
             for (let psIdx = 0; psIdx < data.length; psIdx++) {
@@ -1603,9 +1628,25 @@ module.exports = {
                 // 加分页符(分页符要在外部处理好)-------------------
                 _setupPgBrks(pageDataArray[i]);
                 // 加分页符结束-------------------
+                let maxY = 0;
+                let minY = 100000;
+                const checkAndResetCellYPos = function(cells) {
+                    if (cells && cells.length > 0) {
+                        for (const cell of cells) {
+                            if (maxY < cell[JV.PROP_AREA][JV.PROP_BOTTOM]) {
+                                maxY = cell[JV.PROP_AREA][JV.PROP_BOTTOM];
+                            }
+                            if (minY > cell[JV.PROP_AREA][JV.PROP_TOP]) {
+                                minY = cell[JV.PROP_AREA][JV.PROP_TOP];
+                            }
+                            cell[JV.PROP_AREA][JV.PROP_BOTTOM] += offsetY;
+                            cell[JV.PROP_AREA][JV.PROP_TOP] += offsetY;
+                        }
+                    }
+                };
                 for (let j = 0; j < pageDataArray[i].items.length; j++) {
-                    let maxY = 0;
-                    let minY = 100000;
+                    maxY = 0;
+                    minY = 100000;
                     if (pageDataArray[i].items[j][JV.PAGE_SPECIAL_MERGE_POS]) {
                         let pos = pageDataArray[i].items[j][JV.PAGE_SPECIAL_MERGE_POS][JV.PROP_TOP][0] + offsetY;
                         mergeBand[JV.PROP_TOP].push(pos);
@@ -1621,43 +1662,16 @@ module.exports = {
                         mergeBand[JV.PROP_BOTTOM].push(pageDataArray[i][JV.BAND_PROP_MERGE_BAND][JV.PROP_BOTTOM] + offsetY);
                     }
                     // 1.2.1 重新设置普通cells的Top Bottom坐标
-                    for (const cell of pageDataArray[i].items[j][JV.PROP_CELLS]) {
-                    // for (let k = 0; k < pageDataArray[i].items[j][JV.PROP_CELLS].length; k++) {
-                        if (maxY < cell[JV.PROP_AREA][JV.PROP_BOTTOM]) {
-                            maxY = cell[JV.PROP_AREA][JV.PROP_BOTTOM];
-                        }
-                        if (minY > cell[JV.PROP_AREA][JV.PROP_TOP]) {
-                            minY = cell[JV.PROP_AREA][JV.PROP_TOP];
-                        }
-                        cell[JV.PROP_AREA][JV.PROP_BOTTOM] += offsetY;
-                        cell[JV.PROP_AREA][JV.PROP_TOP] += offsetY;
-                    }
+                    checkAndResetCellYPos(pageDataArray[i].items[j][JV.PROP_CELLS]);
                     // 1.2.2 重新设置电子签名cells的Top Bottom坐标
-                    for (const cell of pageDataArray[i].items[j][JV.PROP_SIGNATURE_CELLS]) {
-                        if (maxY < cell[JV.PROP_AREA][JV.PROP_BOTTOM]) {
-                            maxY = cell[JV.PROP_AREA][JV.PROP_BOTTOM];
-                        }
-                        if (minY > cell[JV.PROP_AREA][JV.PROP_TOP]) {
-                            minY = cell[JV.PROP_AREA][JV.PROP_TOP];
-                        }
-                        // console.log('before');
-                        // console.log(cell);
-                        cell[JV.PROP_AREA][JV.PROP_BOTTOM] += offsetY;
-                        cell[JV.PROP_AREA][JV.PROP_TOP] += offsetY;
-                        // console.log('after');
-                        // console.log(cell);
-                    }
+                    checkAndResetCellYPos(pageDataArray[i].items[j][JV.PROP_SIGNATURE_CELLS]);
                     // 1.2.3 重新设置电子签名日期cells的Top Bottom坐标
-                    for (const cell of pageDataArray[i].items[j][JV.PROP_SIGNATURE_DATE_CELLS]) {
-                        if (maxY < cell[JV.PROP_AREA][JV.PROP_BOTTOM]) {
-                            maxY = cell[JV.PROP_AREA][JV.PROP_BOTTOM];
-                        }
-                        if (minY > cell[JV.PROP_AREA][JV.PROP_TOP]) {
-                            minY = cell[JV.PROP_AREA][JV.PROP_TOP];
-                        }
-                        cell[JV.PROP_AREA][JV.PROP_BOTTOM] += offsetY;
-                        cell[JV.PROP_AREA][JV.PROP_TOP] += offsetY;
-                    }
+                    checkAndResetCellYPos(pageDataArray[i].items[j][JV.PROP_SIGNATURE_DATE_CELLS]);
+                    // 1.2.4 重置电子签名审核意见cells的Top Bottom坐标
+                    checkAndResetCellYPos(pageDataArray[i].items[j][JV.PROP_SIGNATURE_AUDIT_CELLS]);
+                    // 1.2.5 重置水印的Top Bottom坐标
+                    checkAndResetCellYPos(pageDataArray[i].items[j][JV.PROP_WATERMARK_CELLS]);
+
                     const bottomGap = Math.round((pageDataArray[i][JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][1] - parseFloat(pageDataArray[i][JV.NODE_PAGE_INFO][JV.NODE_MARGINS][JV.PROP_BOTTOM]) / 2.54) * DPI) - maxY;
                     offsetY += (maxY - minY);
                     if (bottomGap > 10) {

+ 1 - 0
app/router.js

@@ -346,6 +346,7 @@ module.exports = app => {
     app.get('/tender/:id/measure/material/:order/file/:fid/download', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.downloadFile');
     app.post('/tender/:id/measure/material/:order/file/find', sessionAuth, tenderCheck, uncheckTenderCheck, materialCheck, 'materialController.getCurMatericalFiles');
     app.post('/tender/measure/material/file/delete', sessionAuth, 'materialController.deleteFile');
+    app.get('/tender/:id/measure/material/:order/file/download/compresse-file', sessionAuth, tenderCheck, uncheckTenderCheck, 'materialController.downloadZip');
 
     // 个人账号相关
     app.get('/profile/info', sessionAuth, 'profileController.info');

+ 124 - 75
app/service/material_file.js

@@ -1,75 +1,124 @@
-'use strict';
-const auditConst = require('../const/audit');
-/**
- * 附件表 数据模型
- * @author LanJianRong
- * @date 2020/6/30
- * @version
- */
-
-module.exports = app => {
-    class MaterialFile extends app.BaseService {
-        /**
-         * 构造函数
-         *
-         * @param {Object} ctx - egg全局变量
-         * @return {void}
-         */
-        constructor(ctx) {
-            super(ctx);
-            this.tableName = 'material_file';
-        }
-
-        /**
-         * 获取当前标段(期)所有上传的附件
-         * @param {Number} tid 标段id
-         * @param {Number?} mid 期id
-         * @return {Promise<void>} 数据库查询实例
-         */
-        async getAllMaterialFiles(tid, mid) {
-            const { ctx } = this;
-            const where = { tid };
-            if (mid) where.mid = mid;
-            const result = await this.db.select(this.tableName, {
-                where,
-                orders: [['upload_time', 'desc']],
-            });
-            return result.map(item => {
-                if (!ctx.helper.canPreview(item.fileext)) {
-                    item.filepath = `tender/${ctx.tender.id}/measure/material/${item.s_order}/file/${item.id}/download`;
-                }
-                return item;
-            });
-        }
-
-
-        /**
-         * 存储上传的文件信息至数据库
-         * @param {Array} payload 载荷
-         * @return {Promise<void>} 数据库插入执行实例
-         */
-        async saveFileMsgToDb(payload) {
-            return await this.db.insert(this.tableName, payload);
-        }
-
-        /**
-         * 获取单个文件信息
-         * @param {Number} id 文件id
-         * @return {Promise<void>} 数据库查询实例
-         */
-        async getMaterialFileById(id) {
-            return await this.getDataByCondition({ id });
-        }
-
-        /**
-         * 删除附件
-         * @param {Number} id - 附件id
-         * @return {void}
-         */
-        async delete(id) {
-            return await this.deleteById(id);
-        }
-    }
-    return MaterialFile;
-};
-
+'use strict';
+const archiver = require('archiver');
+const path = require('path');
+const fs = require('fs');
+/**
+ * 附件表 数据模型
+ * @author LanJianRong
+ * @date 2020/6/30
+ * @version
+ */
+
+module.exports = app => {
+    class MaterialFile extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'material_file';
+        }
+
+        /**
+         * 获取当前标段(期)所有上传的附件
+         * @param {Number} tid 标段id
+         * @param {Number?} mid 期id
+         * @return {Promise<void>} 数据库查询实例
+         */
+        async getAllMaterialFiles(tid, mid) {
+            const { ctx } = this;
+            const where = { tid };
+            if (mid) where.mid = mid;
+            const result = await this.db.select(this.tableName, {
+                where,
+                orders: [['upload_time', 'desc']],
+            });
+            return result.map(item => {
+                if (!ctx.helper.canPreview(item.fileext)) {
+                    item.filepath = `tender/${ctx.tender.id}/measure/material/${item.s_order}/file/${item.id}/download`;
+                }
+                return item;
+            });
+        }
+
+
+        /**
+         * 存储上传的文件信息至数据库
+         * @param {Array} payload 载荷
+         * @return {Promise<void>} 数据库插入执行实例
+         */
+        async saveFileMsgToDb(payload) {
+            return await this.db.insert(this.tableName, payload);
+        }
+
+        /**
+         * 获取单个文件信息
+         * @param {Number} id 文件id
+         * @return {Promise<void>} 数据库查询实例
+         */
+        async getMaterialFileById(id) {
+            return await this.getDataByCondition({ id });
+        }
+
+        /**
+         * 删除附件
+         * @param {Number} id - 附件id
+         * @return {void}
+         */
+        async delete(id) {
+            return await this.deleteById(id);
+        }
+
+        /**
+         * 将文件压缩成zip,并返回zip文件的路径
+         * @param {array} fileIds - 文件数组id
+         * @param {string} zipPath - 压缩文件存储路径
+         * @return {string} 压缩后的zip文件路径
+         */
+        async compressedFile(fileIds, zipPath) {
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('id', {
+                value: fileIds,
+                operate: 'in',
+            });
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+            const files = await this.db.query(sql, sqlParam);
+            // const paths = files.map(item => {
+            //     return { name: item.filename + item.fileext, path: item.filepath }
+            // })
+            return new Promise(resolve => {
+                // 每次开一个新的archiver
+                const ziparchiver = archiver('zip');
+                const outputPath = fs.createWriteStream(path.resolve(this.app.baseDir, zipPath));
+                ziparchiver.pipe(outputPath);
+                files.forEach(item => {
+                    ziparchiver.file(path.resolve(this.app.baseDir, 'app', item.filepath), { name: item.file_name });
+                });
+
+                // 存档警告
+                ziparchiver.on('warning', function(err) {
+                    if (err.code === 'ENOENT') {
+                        console.warn('stat故障和其他非阻塞错误');
+                    } else {
+                        throw err;
+                    }
+                });
+
+                // 存档出错
+                ziparchiver.on('error', function(err) {
+                    console.log(err);
+                    throw err;
+                });
+                ziparchiver.finalize();
+                outputPath.on('close', () => {
+                    resolve(ziparchiver.pointer());
+                });
+            });
+        }
+    }
+    return MaterialFile;
+};
+

+ 14 - 2
app/view/material/file.ejs

@@ -5,14 +5,24 @@
       <% include ./material_sub_mini_menu.ejs %>
       <div>
         <div class="d-inline-block">
-          <a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>
+          <div class="dropdown">
+            <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" btn-type="curr" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">当前期</button>
+            <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+              <a class="dropdown-item" href="#">所有期</a>
+            </div>
+          </div>
         </div>
         <div class="d-inline-block">
+          <a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>
+          <a href="javascript: void(0);" data-toggle="modal" class="btn btn-sm btn-light text-primary" id="bach-download"><i class="fa fa-download "></i> 批量下载</a>
+          <a href="" id="downloadZip" style="display: none;" download></a>
+        </div>
+        <!-- <div class="d-inline-block">
           <span class="d-flex align-items-center" style="margin-left: 5px;">
             <input type="checkbox" id="file-checkbox">
             <span class="text-primary" style="margin-left: 5px;">所有期</span>
           </span>
-        </div>
+        </div> -->
       </div>
       <!--<div class="d-flex justify-content-start align-items-center">-->
           <!--<a href="#addfujian" data-toggle="modal" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="添加清单"><i class="fa fa-cloud-upload" aria-hidden="true"></i> 上传附件</a>-->
@@ -29,6 +39,7 @@
         <table class="table table-bordered">
           <thead>
             <tr>
+              <td width="25" style="background-color: #e9ecef;"><input type="checkbox" id="check-all-file" ></td>
               <th width="50">序号</th>
               <th>名称</th>
               <th width="90">大小</th>
@@ -106,6 +117,7 @@
   const auditConst = JSON.parse('<%- JSON.stringify(auditConst) %>');
   const tid = '<%- ctx.tender.id %>';
   const mid = '<%- ctx.material.id %>';
+  const order = '<%- ctx.material.order %>';
   const fileList = JSON.parse('<%- JSON.stringify(fileList) %>');
   const whiteList = JSON.parse('<%- JSON.stringify(whiteList) %>');
 </script>