Przeglądaj źródła

TASK #4087 报表新增签章功能

Tony Kang 3 lat temu
rodzic
commit
a7cad26226

+ 74 - 43
app/controller/report_controller.js

@@ -27,7 +27,7 @@ const needCustomTables = [
     'mem_material_sum_gl',
 ];
 
-const STD_COMP_STAMP_SIZE_WIDTH = 4.2 * 96 / 2.54; // 公章标准尺寸(宽4.2厘米)转成像素
+const STD_COMP_STAMP_SIZE_WIDTH = Math.round(4.2 * 96 / 2.54); // 公章标准尺寸(宽4.2厘米)转成像素
 const STD_COMP_STAMP_SIZE_HEIGHT = STD_COMP_STAMP_SIZE_WIDTH; // 公章标准尺寸(高4.2厘米)转成像素
 const NORMAL_SIGN_STR = 'normal_sign';
 const COMPANY_SIGN_STR = 'company_stamp';
@@ -1497,10 +1497,13 @@ function mergeTextSignature(isTxtSignature, status, pageData, singleRoleRel, rpt
                             area: { Left: sCell.area.Left, Right: sCell.area.Right, Top: sCell.area.Top, Bottom: sCell.area.Bottom },
                         };
                         page.cells.push(newCell); // 迁移
+                        // console.log(newCell);
                         deleteSCellsIdx.push(scIdx);
                     }
                 }
                 // 删除 page.signature_cells 签名(草图不能删);
+                // console.log('isDeleteSignCell: ' + isDeleteSignCell);
+                // console.log(deleteSCellsIdx);
                 if (isDeleteSignCell) {
                     for (let dIdx = deleteSCellsIdx.length - 1; dIdx >= 0; dIdx--) {
                         page.signature_cells.splice(deleteSCellsIdx[dIdx], 1);
@@ -1529,8 +1532,27 @@ function mergeTextSignature(isTxtSignature, status, pageData, singleRoleRel, rpt
 }
 
 // 因机制问题,此方法必须在mergeTextSignature之前调用
-function mergeStampSignature(ctx, status, pageData, singleRoleRel, rpt_ids) {
-    const createStampArea = function(orgCell, controls) {
+async function mergeStampSignature(ctx, status, pageData, singleRoleRel, rpt_ids) {
+    const dupPicPaths = [];
+    const _getMaxRect = function(page) {
+        const rect = [100000000, 100000000, 0, 0];
+        for (const cell of page.cells) {
+            if (rect[0] > cell.area.Left) {
+                rect[0] = cell.area.Left;
+            }
+            if (rect[1] > cell.area.Top) {
+                rect[1] = cell.area.Top;
+            }
+            if (rect[2] < cell.area.Right) {
+                rect[2] = cell.area.Right;
+            }
+            if (rect[3] < cell.area.Bottom) {
+                rect[3] = cell.area.Bottom;
+            }
+        }
+        return rect;
+    };
+    const createStampArea = function(orgCell, controls, maxRect) {
         const ctrl = controls[orgCell.control];
         const rst = { Left: orgCell.area.Left, Right: orgCell.area.Right, Top: orgCell.area.Top, Bottom: orgCell.area.Bottom };
         let pLeft = orgCell.area.Left,
@@ -1563,15 +1585,37 @@ function mergeStampSignature(ctx, status, pageData, singleRoleRel, rpt_ids) {
         rst.Top = pTop;
         rst.Right = pLeft + STD_COMP_STAMP_SIZE_WIDTH;
         rst.Bottom = pTop + STD_COMP_STAMP_SIZE_WIDTH;
+        // 最后一步,如超过报表范围,则要调整坐标
+        // if (rst.Left < maxRect[0]) {
+        //     const width = maxRect[0] - rst.Left;
+        //     rst.Left += width;
+        //     rst.Right += width;
+        // }
+        // if (rst.Top < maxRect[1]) {
+        //     const height = maxRect[1] - rst.Top;
+        //     rst.Top += height;
+        //     rst.Bottom += height;
+        // }
+        // if (rst.Right > maxRect[2]) {
+        //     const width = maxRect[2] - rst.Right; // 负
+        //     rst.Left += width;
+        //     rst.Right += width;
+        // }
+        // if (rst.Bottom > maxRect[3]) {
+        //     const height = maxRect[3] - rst.Bottom;
+        //     rst.Top += height;
+        //     rst.Bottom += height;
+        // }
         return rst;
 
     };
-    const _mergeSingleStamp = function(_page, _roleRelList) {
+    const _mergeSingleStamp = async function(_page, _roleRelList) {
         let roleRelContent = [];
         if (_roleRelList && _roleRelList.rel_content !== null && _roleRelList.rel_content !== undefined && _roleRelList.rel_content !== '') {
             roleRelContent = JSON.parse(_roleRelList.rel_content);
         }
         for (const page of _page.items) {
+            const maxRect = _getMaxRect(page);
             if (page.signature_cells) {
                 const newStampCells = [];
                 for (let scIdx = 0; scIdx < page.signature_cells.length; scIdx++) {
@@ -1584,14 +1628,21 @@ function mergeStampSignature(ctx, status, pageData, singleRoleRel, rpt_ids) {
                                         case COMPANY_SIGN_STR:
                                         case PRIVATE_SIGN_STR:
                                             // 创建一个新的cell
-                                            const stampPath = (signType === COMPANY_SIGN_STR) ? role_rel.company_stamp_path : role_rel.private_stamp_path;
+                                            let stampPath = (signType === COMPANY_SIGN_STR) ? role_rel.company_stamp_path : role_rel.private_stamp_path;
+                                            stampPath = ctx.app.config.fujianOssPath + stampPath;
+                                            if (dupPicPaths.indexOf(stampPath) < 0) {
+                                                dupPicPaths.push(stampPath);
+                                                // await _chkRawPicSizeOSS(ctx, stampPath);
+                                            }
                                             const newStampCell = {
                                                 signature_name: JV.SIGNATURE_NAME_DUMMY,
                                                 control: sCell.control,
                                                 style: sCell.style,
-                                                path: ctx.app.config.fujianOssPath + stampPath,
+                                                path: stampPath,
                                                 isStamp: true,
-                                                area: createStampArea(sCell, _page[JV.NODE_CONTROL_COLLECTION]),
+                                                maxRect,
+                                                orgArea: sCell.area,
+                                                area: createStampArea(sCell, _page[JV.NODE_CONTROL_COLLECTION], maxRect),
                                             };
                                             newStampCells.push(newStampCell);
                                             break;
@@ -1775,6 +1826,22 @@ async function _chkRawPicSize(pageData, baseDir) {
     return rst;
 }
 
+async function _chkRawPicSizeOSS(ctx, picPath) {
+    // 计量的草图、签章都放在OSS服务器上,需要另外的方式来获取,考虑到效率问题,机制上得有所变化
+    const rst = [0, 0];
+    try {
+        const pf = await ctx.app.signPdfOss.get(picPath);
+        console.log('get pic successfully!');
+        console.log(pf);
+    } catch (ex) {
+        console.log(ex);
+    } finally {
+        console.log('finally!');
+    }
+    return rst;
+}
+
+
 function _resetPageDataByBreaks(pageDataArr, breakAmt, rpt_names, newRptNames) {
     const rst = [];
     for (let pi = 0; pi < pageDataArr.length; pi++) {
@@ -1809,39 +1876,3 @@ function _resetPageDataByBreaks(pageDataArr, breakAmt, rpt_names, newRptNames) {
     }
     return rst;
 }
-
-function _resetStampPos(orgCell, targetCell, controls) {
-    const ctrl = controls[orgCell.control];
-    let pLeft = orgCell.area.Left,
-        pTop = orgCell.area.Top;
-    switch (ctrl[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_HORIZON]]) {
-        case JV.OUTPUT_ALIGN.H[JV.H_ALIGN_IDX_LEFT]:
-            pLeft = orgCell.area.Left;
-            break;
-        case JV.OUTPUT_ALIGN.H[JV.H_ALIGN_IDX_CENTER]:
-            pLeft = (orgCell.area.Left + orgCell.area.Right - STD_STAMP_SIZE_WIDTH) / 2;
-            break;
-        case JV.OUTPUT_ALIGN.H[JV.H_ALIGN_IDX_RIGHT]:
-            pLeft = orgCell.area.Right - STD_STAMP_SIZE_WIDTH;
-            break;
-        default:
-            break;
-    }
-    switch (ctrl[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]]) {
-        case JV.OUTPUT_ALIGN.H[JV.V_ALIGN_IDX_TOP]:
-            pTop = orgCell.area.Top;
-            break;
-        case JV.OUTPUT_ALIGN.H[JV.V_ALIGN_IDX_CENTER]:
-            pTop = (orgCell.area.Top + orgCell.area.Bottom - STD_STAMP_SIZE_HEIGHT) / 2;
-            break;
-        case JV.OUTPUT_ALIGN.H[JV.V_ALIGN_IDX_BOTTOM]:
-            pTop = orgCell.area.Bottom - STD_STAMP_SIZE_HEIGHT;
-            break;
-        default:
-            break;
-    }
-    targetCell.area.Left = pLeft;
-    targetCell.area.Top = pTop;
-    targetCell.area.Right = pLeft + STD_STAMP_SIZE_WIDTH;
-    targetCell.area.Bottom = pTop + STD_STAMP_SIZE_HEIGHT;
-}

+ 52 - 2
app/public/report/js/jpc_output.js

@@ -543,7 +543,7 @@ let JpcCanvasOutput = {
             if (page.signature_cells && page.signature_cells.length > 0) {
                 for (let k = 0; k < page.signature_cells.length; k++) {
                     let cell = page.signature_cells[k];
-                    if (PAGE_SHOW['isTextSignature'] === 0 || cell.signature_name === JV.SIGNATURE_NAME_DUMMY) {
+                    if (PAGE_SHOW['isTextSignature'] === 0 || cell.signature_name.indexOf(JV.SIGNATURE_NAME_DUMMY) >= 0) {
                         // 如果是非文本签名或草图,才显示图片(文本签名已经在后台单独处理,在cells数组内增加一个合适的cell)
                         private_drawSignatureCell(cell, fonts, styles, controls, newPageMergeBand);
                     }
@@ -597,6 +597,40 @@ let JpcCanvasOutput = {
         ctx.fillRect((size[0] + me.offsetX) * me.scaleFactor, (10 + me.offsetY) * me.scaleFactor, 10 * me.scaleFactor, size[1] * me.scaleFactor);
         ctx.fillRect((10 + me.offsetX) * me.scaleFactor, (size[1] + me.offsetY) * me.scaleFactor, size[0] * me.scaleFactor, 10 * me.scaleFactor);
     },
+    highlightConflictArea: function (pageObj, pageIdx) {
+        const _chkAndSetConflict = function(orgCells) {
+            let conflictIds = [];
+            for (let j = 0; j < orgCells.length; j++) {
+                if (conflictIds.indexOf(j) < 0) {
+                    let cell1 = orgCells[j];
+                    for (let k = j + 1; k < orgCells.length; k++) {
+                        let cell2 = orgCells[k];
+                        //判断area是否有交叉
+                        if (areaConflict(cell1[JV.PROP_AREA], cell2[JV.PROP_AREA])) {
+                            conflictIds.push(j);
+                            conflictIds.push(k);
+                            cell1[JV.PROP_STYLE] = "ConflictCell";
+                            cell2[JV.PROP_STYLE] = "ConflictCell";
+                        }
+                    }
+                }
+            }
+        };
+        if (pageObj && pageObj.items.length > 0 && pageObj.items.length >= pageIdx) {
+            let private_create_conflict_line = function () {
+                return {"LineWeight": "1", "DashStyle": "SOLID", "Color": "RED"};
+            };
+            let page = pageObj.items[pageIdx - 1];
+            if (!pageObj[JV.NODE_STYLE_COLLECTION].hasOwnProperty('ConflictCell')) {
+                const styleConflict = {"Left": private_create_conflict_line(), "Right": private_create_conflict_line(), "Top": private_create_conflict_line(), "Bottom": private_create_conflict_line()};
+                pageObj[JV.NODE_STYLE_COLLECTION]["ConflictCell"] = styleConflict;
+            }
+            _chkAndSetConflict(page.cells);
+            _chkAndSetConflict(page.signature_date_cells);
+            _chkAndSetConflict(page.signature_audit_cells);
+            _chkAndSetConflict(page.signature_cells);
+        }
+    },
     getReportSizeInPixel: function(rptTpl, resolution) {
         let rst = [8.27, 11.69];
         if (rptTpl && rptTpl[JV.NODE_PAGE_INFO] && rptTpl[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE]) {
@@ -606,4 +640,20 @@ let JpcCanvasOutput = {
         rst[1] = Math.round(resolution[0] * rst[1]);
         return rst;
     }
-};
+};
+
+function areaConflict(area1,area2) {
+    let maxX,maxY,minX,minY,
+        w1 = area1[JV.PROP_RIGHT] - area1[JV.PROP_LEFT],
+        w2 = area2[JV.PROP_RIGHT] - area2[JV.PROP_LEFT],
+        h1 = area1[JV.PROP_BOTTOM] - area1[JV.PROP_TOP],
+        h2 = area2[JV.PROP_BOTTOM] - area2[JV.PROP_TOP]
+    ;
+    //1. 求2个矩形的最小外包矩形
+    minX = (area1[JV.PROP_LEFT] <= area2[JV.PROP_LEFT]) ? area1[JV.PROP_LEFT] : area2[JV.PROP_LEFT];
+    minY = (area1[JV.PROP_TOP] <= area2[JV.PROP_TOP]) ? area1[JV.PROP_TOP] : area2[JV.PROP_TOP];
+    maxX = (area1[JV.PROP_RIGHT] >= area2[JV.PROP_RIGHT]) ? area1[JV.PROP_RIGHT] : area2[JV.PROP_RIGHT];
+    maxY = (area1[JV.PROP_BOTTOM] >= area2[JV.PROP_BOTTOM]) ? area1[JV.PROP_BOTTOM] : area2[JV.PROP_BOTTOM];
+    //2. 判断外包矩形与高与宽是否小于俩矩形的高与宽之和(这里的边界条件是小于,等于的话不算;而且是高与宽都得符合条件!)
+    return (maxX - minX < w1 + w2 && maxY - minY < h1 + h2);
+}

+ 8 - 0
app/public/report/js/jpc_output_value_define.js

@@ -69,6 +69,14 @@ let JV = {
     FONT_PROP_IDX_STRIKEOUT: 6,
     FONT_PROP_IDX_ANGLE: 7,
 
+    PROP_PAGE_SEQ: 'page_seq',
+    PROP_CELLS: 'cells',
+    PROP_WATERMARK_CELLS: 'watermark_cells',
+    PROP_SIGNATURE_CELLS: 'signature_cells',
+    PROP_SIGNATURE_DATE_CELLS: 'signature_date_cells',
+    PROP_SIGNATURE_AUDIT_CELLS: 'signature_audit_cells',
+    PAGE_SPECIAL_MERGE_POS: "page_merge_pos",
+
     PAGES_SIZE_STR: ['A3', 'A4', 'A5', 'B4', 'B5', 'LETTER', 'LEGAL', 'EXECUTIVE', '16K'],
     PAGES_SIZE_IDX: [8, 9, 11, 12, 13, 1, 5, 7, 93],
     PAGES_SIZE: [[11.693, 16.535], [8.268, 11.693], [5.827, 8.268], [9.84, 13.898], [6.93, 9.84], [8.5, 11.0], [8.5, 14.0], [7.25, 10.5], [7.25, 10.5]],

+ 1 - 0
app/public/report/js/rpt_archive.js

@@ -793,6 +793,7 @@ function getBlob(url) {
     return new Promise(resolve => {
         const xhr = new XMLHttpRequest();
 
+        // let fullUrl = url + '?x-oss-process=image/info';
         xhr.open('GET', url, true);
         xhr.responseType = 'blob';
         xhr.onload = () => {

Plik diff jest za duży
+ 1778 - 0
app/public/report/js/rpt_jsexcel.js


+ 3 - 3
app/public/report/js/rpt_jspdf.js

@@ -153,7 +153,7 @@ let JpcJsPDFHelper = {
                 private_drawLine(cell, doc, ctx, style, JV.PROP_LEFT, [JV.PROP_LEFT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_TOP], mergedBand, styles, isNeedMergeBand);
             }
             ctx.closePath();
-            if (!onlyShowBorder || cell.signature_name === JV.SIGNATURE_NAME_DUMMY) private_drawSignatureCellText(doc, ctx, cell, controls, signatureRelArr);
+            if (!onlyShowBorder || cell.signature_name.indexOf(JV.SIGNATURE_NAME_DUMMY) >= 0) private_drawSignatureCellText(doc, ctx, cell, controls, signatureRelArr);
         }
 
         function private_drawSignatureCellText(doc, ctx, cell, controls, signatureRelArr) {
@@ -164,7 +164,7 @@ let JpcJsPDFHelper = {
                 control = cell[JV.PROP_CONTROL];
             }
             if (cell.pic) {
-                if (cell.signature_name === JV.SIGNATURE_NAME_DUMMY) {
+                if (cell.signature_name.indexOf(JV.SIGNATURE_NAME_DUMMY) >= 0) {
                     private_drawImage(doc, ctx, cell, control, cell.pic, [1, 1, 1, 1]);
                 } else {
                     private_drawImage(doc, ctx, cell, control, cell.pic);
@@ -229,7 +229,7 @@ let JpcJsPDFHelper = {
 
         function private_drawImage(doc, ctx, cell, control, imageData, offsetArea) {
             let area = private_getProperSignatureArea(cell, control);
-            if (cell.signature_name === JV.SIGNATURE_NAME_DUMMY) {
+            if (cell.signature_name.indexOf(JV.SIGNATURE_NAME_DUMMY) >= 0) {
                 area[0] = cell[JV.PROP_AREA][JV.PROP_LEFT] + 1 + offsetX; // Left
                 area[1] = cell[JV.PROP_AREA][JV.PROP_TOP] + 1 + offsetX; // Top
                 area[2] = cell[JV.PROP_AREA][JV.PROP_RIGHT] - 1 + offsetX; // Right

+ 122 - 9
app/public/report/js/rpt_main.js

@@ -260,7 +260,7 @@ let zTreeOprObj = {
             zTreeOprObj.treeObj.expandNode(treeNode, true, true, false);
         }
     },
-    onClick: function(event,treeId,treeNode) {
+    onClick: async function(event,treeId,treeNode) {
         let me = zTreeOprObj;
         if (treeNode && treeNode.nodeType === TPL_TYPE_TEMPLATE && treeNode.refId > 0) {
             window.history.pushState({},0, window.location.pathname + `?rpt_id=${treeNode.refId}`);
@@ -314,7 +314,7 @@ let zTreeOprObj = {
             }
 
             rptArchiveObj.toggleBtn(true);
-            me.requestNormalReport(params);
+            await me.requestNormalReport(params);
             me.countChkedRptTpl();
             rptCustomObj.showMaterialSelect();
         }
@@ -336,7 +336,7 @@ let zTreeOprObj = {
         dom.innerHTML = tmpStr;
         me.changeCfg();
     },
-    changeCfg: function() {
+    changeCfg: async function() {
         let me = zTreeOprObj;
         if (me.currentNode) {
             CUST_CFG_ORG = JSON.parse(JSON.stringify(CUST_CFG));
@@ -389,7 +389,7 @@ let zTreeOprObj = {
                 return;
             }
 
-            me.requestNormalReport(params);
+            await me.requestNormalReport(params);
             me.countChkedRptTpl();
             rptCustomObj.showMaterialSelect();
 
@@ -417,7 +417,7 @@ let zTreeOprObj = {
     _setupArchive: function() {
         //
     },
-    requestNormalReport: function (params) {
+    requestNormalReport: async function (params) {
         let me = zTreeOprObj;
         if (COMMON_WATER_MARK_PIC_DATA === null || COMMON_WATER_MARK_PIC_DATA === '') {
             params.needWaterMark = true;
@@ -426,7 +426,7 @@ let zTreeOprObj = {
         }
         $.bootstrapLoading.start();
         CommonAjax.postXsrfEx("/tender/report_api/getReport", params, 300000, true, getCookie('csrfToken_j'),
-            function(result){
+            async function(result){
                 $.bootstrapLoading.end();
                 let pageRst = result.data;
                 if (params.needWaterMark) COMMON_WATER_MARK_PIC_DATA = result.waterMarkStr;
@@ -441,6 +441,7 @@ let zTreeOprObj = {
                     if (current_stage_status === 3) {
                         rptSignatureHelper.mergeSignDate(pageRst, ROLE_REL_LIST, true);
                         rptSignatureHelper.mergeSignature(pageRst, ROLE_REL_LIST);
+                        await rptSignatureHelper.resetDummySignature(pageRst, ROLE_REL_LIST); // 这里重新整理签章坐标信息(因签章大小在后台暂时获取不到,挪到前端处理)
                         rptSignatureHelper.mergeSignAudit(pageRst, ROLE_REL_LIST, STAGE_AUDIT);
                     }
                 } else {
@@ -521,6 +522,7 @@ let zTreeOprObj = {
             me.currentPage = pageNum;
             JpcCanvasOutput.cleanCanvas(canvas);
             JpcCanvasOutput.drawPageBorder(me.currentRptPageRst, canvas, getScreenDPI());
+            // JpcCanvasOutput.highlightConflictArea(me.currentRptPageRst, pageNum);
             JpcCanvasOutput.drawToCanvas(me.currentRptPageRst, canvas, me.currentPage);
         }
         me.displayPageValue();
@@ -719,13 +721,121 @@ let rptControlObj = {
             );
         }
     },
+
+    downloadExcelReport: async function(pageDataArr, pageSize, rpt_names) {
+        const private_download = async function(currentIndex) {
+            if (currentIndex < pageDataArr.length) {
+                //这里的数据应该在调用前己处理
+                const rptName = rpt_names[currentIndex];
+                const singlePage = true;
+                await excelExportUtil.exportExcel(pageDataArr[currentIndex], pageSize, rptName, singlePage, null, null, ROLE_REL_LIST, null);
+            }
+        };
+        for (let idx = 0; idx < pageDataArr.length; idx++) {
+            await private_download(idx);
+        }
+    },
+
+    downloadExcelReportInOneBook: async function(pageDataArr, pageSize, signatureRelArr, signatureRelInfo, refRptTplIds, rpt_names) {
+        //
+    },
+    
+    getExcel_New: async function (isOneBook) {
+        let me = rptControlObj;
+        if (zTreeOprObj.checkedRptTplNodes && zTreeOprObj.checkedRptTplNodes.length > 0 && PAGE_SHOW['closeExportExcel'] !== 1) {
+            let refRptTplIds = [];
+            let rpt_names = [];
+            rptControlObj.getTplIdsCommon(refRptTplIds, rpt_names);
+            const signatureRelArr = [];
+            if (refRptTplIds.length === 0) {
+                if (zTreeOprObj.currentNode) {
+                    //在复杂情况下会影响到原有数据(文本签名 + 签章),为减少麻烦,还是当勾选处理
+                    refRptTplIds.push(zTreeOprObj.currentNode.refId);
+                    rpt_names.push(zTreeOprObj.currentNode.name);
+                }
+            }
+            if (refRptTplIds.length > 0) {
+                let params = rptControlObj.creatCommonExportParam(refRptTplIds);
+                // params.getPicFlag = true; //专门针对草图项,只有此项为true,才需要把草图信息带过来,预览及打印动态加载草图 // 纠结:但这样还是解决不了效率问题,得另外想交互方式
+                await rptCustomObj.getCustomSelect(params);
+                delete params.orientation; // 打印时有勾选的话,不需要提供方向
+                $.bootstrapLoading.start();
+                if (COMMON_WATER_MARK_PIC_DATA === null || COMMON_WATER_MARK_PIC_DATA === '') {
+                    params.needWaterMark = true;
+                } else {
+                    params.needWaterMark = false;
+                }
+                CommonAjax.postXsrfEx("/tender/report_api/getMultiReports", params, WAIT_TIME_EXPORT, true, getCookie('csrfToken_j'),
+                    function(result){
+                        $.bootstrapLoading.end();
+                        if (params.needWaterMark) COMMON_WATER_MARK_PIC_DATA = result.waterMarkStr;
+                        STAGE_AUDIT = result.stageAudit;
+                        let pageSize = rptControlObj.getCurrentPageSize();
+                        let pageDataArr = result.data;
+                        let signatureRelInfo = result.signatureRelInfo;
+                        for (const signatureRel of signatureRelInfo) {
+                            const tmpRel = [];
+                            tmpRel.push(JSON.parse(signatureRel.rel_content));
+                            signatureRelArr.push(tmpRel);
+                        }
+                        if (current_stage_status === 3) {
+                            // 统一安排merge(除草图外)
+                            for (let pageObj of pageDataArr) {
+                                let singleSignatureRelArr = [];
+                                for (let rIdx = 0; rIdx < signatureRelInfo.length; rIdx++) {
+                                    let rptId = refRptTplIds[rptControlObj.currentDownloadIdx];
+                                    if (signatureRelInfo[rIdx].rpt_id === rptId) {
+                                        singleSignatureRelArr = signatureRelArr[rIdx]; // 有些报表可能没有签名
+                                        break;
+                                    }
+                                }
+                                rptSignatureHelper.mergeSignDate(pageObj, singleSignatureRelArr, false);
+                                // rptSignatureHelper.mergeSignature(pageObj, signatureRelArr); // 这里merge的意义不大
+                                rptSignatureHelper.mergeSignAudit(pageObj, singleSignatureRelArr, STAGE_AUDIT);
+                            }
+                        }
+
+                        if (isOneBook) {
+                            me.downloadExcelReportInOneBook(pageDataArr, pageSize, signatureRelArr, signatureRelInfo, refRptTplIds, rpt_names);
+                        } else {
+                            // 本地处理
+                            me.downloadExcelReport(pageDataArr, pageSize, rpt_names);
+                        }
+                    },
+                    function(failRst){
+                        $.bootstrapLoading.end();
+                        console.log(failRst);
+                    },
+                    function(exceptionRst){
+                        $.bootstrapLoading.end();
+                        console.log(exceptionRst);
+                    }
+                );
+            } else {
+                // 这个分支本来是为了减少请求,用户已经点过的表,又没有勾选,那么就直接导出EXCEL
+                // 但:发现在复杂情况下会影响到原有数据(文本签名 + 签章),为减少麻烦,在前面处理,保证不会走到这分支!
+            }
+        }
+    },
+
+    getExcel_SingleSelected: async function() {
+        if (zTreeOprObj.currentRptPageRst) {
+            // 新处理(前端生成excel报表)
+            const pageSize = rptControlObj.getCurrentPageSize();
+            const rptName = zTreeOprObj.currentNode.name;
+            const singlePage = true;
+            excelExportUtil.exportExcel(zTreeOprObj.currentRptPageRst, pageSize, rptName, singlePage, null, null, ROLE_REL_LIST, null);
+        }
+    },
     checkAndGetExcel: function () {
         if (zTreeOprObj.treeObj) {
             let chkNodes = zTreeOprObj.treeObj.getCheckedNodes(true);
             if (chkNodes.length > 0) {
                 $("#show_excel_output_cfg").trigger("click");
             } else {
-                rptControlObj.getAllIndividualExcelBook();
+                // rptControlObj.getAllIndividualExcelBook();
+                // rptControlObj.getExcel_SingleSelected();
+                rptControlObj.getExcel_New(false);
             }
         }
     },
@@ -800,7 +910,7 @@ let rptControlObj = {
                     params.needWaterMark = false;
                 }
                 CommonAjax.postXsrfEx("/tender/report_api/getMultiReports", params, WAIT_TIME_EXPORT, true, getCookie('csrfToken_j'),
-                    function(result){
+                    async function(result){
                         // closeWaitingView();
                         $.bootstrapLoading.end();
                         if (params.needWaterMark) COMMON_WATER_MARK_PIC_DATA = result.waterMarkStr;
@@ -809,6 +919,9 @@ let rptControlObj = {
                         for (const signatureRel of result.signatureRelInfo) {
                             signatureRelArr.push(JSON.parse(signatureRel.rel_content));
                         }
+                        for (const pageData of result.data) {
+                            await rptSignatureHelper.resetDummySignature(pageData, null); //
+                        }
                         downloadPDFReport(result.data, pageSize, rpt_names, signatureRelArr, result.signatureRelInfo, refRptTplIds, STAGE_AUDIT);
                         /*
                         for (let idx = 0; idx < result.data.length; idx++) {
@@ -1059,7 +1172,7 @@ function downloadPDFReport(pageDataArr, pageSize, rpt_names, signatureRelArr, si
     for (let pageData of pageDataArr) {
         for (let page of pageData.items) {
             for (let dCell of page.signature_cells) {
-                if (dCell.signature_name === 'dummy_pic') {
+                if (dCell.signature_name.indexOf('dummy_pic') >= 0) {
                     let picIdx = picPaths.indexOf(dCell.path);
                     if (picIdx < 0) {
                         picPaths.push(dCell.path);

+ 3 - 2
app/public/report/js/rpt_preview_common.js

@@ -27,7 +27,7 @@ Date.prototype.Format = function(fmt) {
     return fmt;
 };
 
-function printPageLoading() {
+async function printPageLoading() {
     let params = JSON.parse(sessionStorage.report_params);
     let _current_stage_status = parseInt(sessionStorage.current_stage_status);
     let closeWaterMark = parseInt(sessionStorage.closeWaterMark);
@@ -38,7 +38,7 @@ function printPageLoading() {
     current_stage_id = parseInt(sessionStorage.current_stage_id);
     let scaleFactor = 1;
     CommonAjax.postXsrfEx("/tender/report_api/getMultiReports", params, 60000, true, getCookie('csrfToken_j'),
-        function(result){
+        async function(result){
             const signatureRelArr = [];
             STAGE_AUDIT = result.stageAudit;
             // sessionStorage.waterMarkStr = result.waterMarkStr;
@@ -58,6 +58,7 @@ function printPageLoading() {
                 if (_current_stage_status === 3) {
                     rptSignatureHelper.mergeSignDate(result.data[idx], singleSignatureRelArr, false);
                     rptSignatureHelper.mergeSignature(result.data[idx], singleSignatureRelArr);
+                    await rptSignatureHelper.resetDummySignature(result.data[idx], null); // 这里重新整理签章坐标信息(因签章大小在后台暂时获取不到,挪到前端处理)
                     rptSignatureHelper.mergeSignAudit(result.data[idx], singleSignatureRelArr, result.stageAudit);
                 }
             }

+ 1 - 1
app/public/report/js/rpt_print.js

@@ -344,7 +344,7 @@ function buildImage(destRst, cell, control, offsetX, offsetY, adjustY, isHtoV, H
         href = 'href="' + cell.pic +'"';
     }
     const area = getProperSignatureArea(cell, control, offsetX, offsetY);
-    if (cell.signature_name === JV.SIGNATURE_NAME_DUMMY) {
+    if (cell.signature_name.indexOf(JV.SIGNATURE_NAME_DUMMY) >= 0) {
         area[0] = cell[JV.PROP_AREA][JV.PROP_LEFT] + 1 + offsetX; // Left
         area[1] = cell[JV.PROP_AREA][JV.PROP_TOP] + 1 + offsetX; // Top
         area[2] = cell[JV.PROP_AREA][JV.PROP_RIGHT] - 1 + offsetX; // Right

+ 117 - 4
app/public/report/js/rpt_signature.js

@@ -219,7 +219,7 @@ let rptSignatureHelper = {
             for (const page of pageRst.items) {
                 if (page.signature_cells) {
                     for (const sCell of page.signature_cells) {
-                        if (sCell.signature_name !== null && sCell.signature_name !== undefined && sCell.signature_name !== 'dummy_pic') {
+                        if (sCell.signature_name !== null && sCell.signature_name !== undefined && sCell.signature_name.indexOf(JV.SIGNATURE_NAME_DUMMY) < 0) {
                             if (singatureNameArr.indexOf(sCell.signature_name) < 0) {
                                 signature_cells.push(sCell);
                                 singatureNameArr.push(sCell.signature_name);
@@ -313,16 +313,17 @@ let rptSignatureHelper = {
         elementsStrArr.push(`           <label class="form-check-label" for="${idSuffixStr}_sign1">签字</label>`);
         elementsStrArr.push('       </div>');
         const hasIndividualStamp = (userAcc.stamp_path && userAcc.stamp_path !== ''); //用户账号的stamp_path是属于用户自己的私章,不是公司章
-        let chkType = hasIndividualStamp ? 'radio' : 'checkbox';
+        let chkType = hasIndividualStamp ? `radio` : 'checkbox';
+        let rdoNameStr = `dtp_${role_rel.signature_name}_${rptSignatureHelper.currentSelectedESignParentDivId}`;
         if (hasIndividualStamp) {
             elementsStrArr.push('       <div class="form-check form-check-inline mx-1">');
             elementsStrArr.push('           <div class="form-group">');
             elementsStrArr.push('               <div class="form-check form-check-inline px-1">');
-            elementsStrArr.push(`                   <input class="form-check-input" type="${chkType}" id="${idSuffixStr}_sign2" value="companyStamp" name="${idSuffixStr}" onchange="rptSignatureHelper._changeSignType(this, ${userAcc.id}, '${COMPANY_SIGN_STR}')" ${companySignChkStr}>`);
+            elementsStrArr.push(`                   <input class="form-check-input" type="${chkType}" id="${idSuffixStr}_sign2" value="companyStamp" name="${rdoNameStr}" onchange="rptSignatureHelper._changeSignType(this, ${userAcc.id}, '${COMPANY_SIGN_STR}')" ${companySignChkStr}>`);
             elementsStrArr.push(`                   <label class="form-check-label" for="${idSuffixStr}_rdSign1">单位章</label>`);
             elementsStrArr.push('               </div>');
             elementsStrArr.push('               <div class="form-check form-check-inline">');
-            elementsStrArr.push(`                   <input class="form-check-input" type="${chkType}" id="${idSuffixStr}_sign3" value="individualStamp" name="${idSuffixStr}" onchange="rptSignatureHelper._changeSignType(this, ${userAcc.id}, '${PRIVATE_SIGN_STR}')" ${privateSignChkStr}>`);
+            elementsStrArr.push(`                   <input class="form-check-input" type="${chkType}" id="${idSuffixStr}_sign3" value="individualStamp" name="${rdoNameStr}" onchange="rptSignatureHelper._changeSignType(this, ${userAcc.id}, '${PRIVATE_SIGN_STR}')" ${privateSignChkStr}>`);
             elementsStrArr.push(`                   <label class="form-check-label" for="${idSuffixStr}_sign3">个人章</label>`);
             elementsStrArr.push('               </div>');
             elementsStrArr.push('           </div>');
@@ -690,6 +691,99 @@ let rptSignatureHelper = {
             }
         }
     },
+    resetDummySignature: async function(pageData, roleRel, ifPushRoleRel = false) {
+        // 备注:计算草图等其他图形需要额外做些处理
+        let dummySignIdx = 0;
+        const stampPicKeys = [], stampPicFeatures = [];
+        const _resetStampArea = function(stampCell, roleRelItem) {
+            const ctrl = pageData[JV.NODE_CONTROL_COLLECTION][stampCell[JV.PROP_CONTROL]];
+            let pLeft = stampCell.orgArea.Left,
+                pTop = stampCell.orgArea.Top;
+            let std_stamp_size_width = STD_STAMP_SIZE_WIDTH, std_stamp_size_height = STD_STAMP_SIZE_HEIGHT;
+            if (roleRelItem.stampFeature) {
+                std_stamp_size_width = parseFloat(roleRelItem.stampFeature.ImageWidth.value);
+                std_stamp_size_height = parseFloat(roleRelItem.stampFeature.ImageHeight.value);
+            }
+            switch (ctrl[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_HORIZON]]) {
+                case JV.OUTPUT_ALIGN.H[JV.H_ALIGN_IDX_LEFT]:
+                    pLeft = stampCell.orgArea.Left;
+                    break;
+                case JV.OUTPUT_ALIGN.H[JV.H_ALIGN_IDX_CENTER]:
+                    pLeft = (stampCell.orgArea.Left + stampCell.orgArea.Right - std_stamp_size_width) / 2;
+                    break;
+                case JV.OUTPUT_ALIGN.H[JV.H_ALIGN_IDX_RIGHT]:
+                    pLeft = stampCell.orgArea.Right - std_stamp_size_width;
+                    break;
+                default:break;
+            }
+            switch (ctrl[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]]) {
+                case JV.OUTPUT_ALIGN.H[JV.V_ALIGN_IDX_TOP]:
+                    pTop = stampCell.orgArea.Top;
+                    break;
+                case JV.OUTPUT_ALIGN.H[JV.V_ALIGN_IDX_CENTER]:
+                    pTop = (stampCell.orgArea.Top + stampCell.orgArea.Bottom - std_stamp_size_height) / 2;
+                    break;
+                case JV.OUTPUT_ALIGN.H[JV.V_ALIGN_IDX_BOTTOM]:
+                    pTop = stampCell.orgArea.Bottom - std_stamp_size_height;
+                    break;
+                default:break;
+            }
+            stampCell.area.Left = pLeft;
+            stampCell.area.Top = pTop;
+            stampCell.area.Right = pLeft + std_stamp_size_width;
+            stampCell.area.Bottom = pTop + std_stamp_size_height;
+            // 最后一步,如超过报表范围,则要调整坐标
+            const maxRect = stampCell.maxRect;
+            if (stampCell.area.Left < maxRect[0]) {
+                const width = maxRect[0] - stampCell.area.Left;
+                stampCell.area.Left += width;
+                stampCell.area.Right += width;
+            }
+            if (stampCell.area.Top < maxRect[1]) {
+                const height = maxRect[1] - stampCell.area.Top;
+                stampCell.area.Top += height;
+                stampCell.area.Bottom += height;
+            }
+            if (stampCell.area.Right > maxRect[2]) {
+                const width = maxRect[2] - stampCell.area.Right; // 负
+                stampCell.area.Left += width;
+                stampCell.area.Right += width;
+            }
+            if (stampCell.area.Bottom > maxRect[3]) {
+                const height = maxRect[3] - stampCell.area.Bottom;
+                stampCell.area.Top += height;
+                stampCell.area.Bottom += height;
+            }
+        };
+        for (let pageIdx = 0; pageIdx < pageData.items.length; pageIdx++) {
+            const page = pageData.items[pageIdx];
+            if (page[JV.PROP_SIGNATURE_CELLS] && page[JV.PROP_SIGNATURE_CELLS].length > 0) {
+                for (const signatureCell of page[JV.PROP_SIGNATURE_CELLS]) {
+                    if (signatureCell.signature_name.indexOf(JV.SIGNATURE_NAME_DUMMY) >= 0) {
+                        // 表示这是一个其他类型的非原生电子签名图,只是借用signature的处理机制,每个图都是唯一的,所以需要重新给个唯一的新signature_name
+                        // signatureCell.signature_name = signatureCell.signature_name + '_' + pageIdx + '_' + dummySignIdx;
+                        signatureCell.signature_name = signatureCell.signature_name + '_' + page.page_seq + '_' + dummySignIdx; // page_seq在分页后都不会变动
+                        dummySignIdx++;
+                        const roleRelItem = { type: '用户', sign_path: signatureCell.path, signature_name: signatureCell.signature_name };
+                        if (signatureCell.isStamp) {
+                            let stmpIdx = stampPicKeys.indexOf(signatureCell.path);
+                            if (stampPicKeys.indexOf(signatureCell.path) < 0) {
+                                stampPicKeys.push(signatureCell.path);
+                                stmpIdx = stampPicKeys.length - 1;
+                                const picRes = await getHttpBlobText(signatureCell.path + '?x-oss-process=image/info');
+                                stampPicFeatures.push(picRes);
+                            }
+                            // { "FileSize": {"value": "2514"}, "Format": {"value": "png"}, "ImageHeight": {"value": "94"}, "ImageWidth": {"value": "94"} }
+                            roleRelItem.stampFeature = stampPicFeatures[stmpIdx];
+                            // 重新处理cell坐标
+                            _resetStampArea(signatureCell, roleRelItem);
+                        }
+                        if (ifPushRoleRel) roleRel.push(roleRelItem);
+                    }
+                }
+            }
+        }
+    },
     mergeSignAudit: function (pageData, currRoleRelList, currAuditList) {
         for (const page of pageData.items) {
             if (page.signature_audit_cells) {
@@ -949,3 +1043,22 @@ function resetTextSignature(pageData) {
         }
     }
 }
+
+function getHttpBlobText(url) {
+    return new Promise(resolve => {
+        const xhr = new XMLHttpRequest();
+
+        // let fullUrl = url + '?x-oss-process=image/info';
+        xhr.open('GET', url, true);
+        xhr.responseType = 'json';
+        xhr.onload = () => {
+            if (xhr.status === 200) {
+                resolve(xhr.response);
+            } else {
+                resolve('not found!');
+            }
+        };
+
+        xhr.send();
+    });
+}

+ 3 - 0
app/view/report/index.ejs

@@ -313,6 +313,8 @@
 <script src="/public/js/datepicker/datepicker.min.js"></script>
 <script src="/public/js/datepicker/datepicker.zh.js"></script>
 <script src="/public/js/shares/cs_tools.js"></script>
+<script src="/public/js/js-xlsx/jszip.min.js"></script>
+<script src="/public/js/file-saver/FileSaver.js"></script>
 
 <!-- zTree -->
 <script type="text/javascript" src="/public/js/ztree/jquery.ztree.core.js"></script>
@@ -335,6 +337,7 @@
 <script type="text/javascript" src="/public/report/js/rpt_print.js"></script>
 <script type="text/javascript" src="/public/report/js/rpt_signature.js"></script>
 <script type="text/javascript" src="/public/report/js/rpt_jspdf.js"></script>
+<script type="text/javascript" src="/public/report/js/rpt_jsexcel.js"></script>
 <script type="text/javascript" src="/public/report/js/rpt_indexDb.js"></script>
 
 <!--