Browse Source

code sync

TonyKang 5 năm trước cách đây
mục cha
commit
24aad84aed

+ 145 - 0
modules/controllers/rpt_controller.js

@@ -0,0 +1,145 @@
+/**
+ * Created by Tony on 2017/3/13.
+ */
+
+let fsUtil = require("../../public/fsUtil");
+
+const rpt_pdf_util = require("../output/rpt_pdf_util");
+const fs = require('fs') ;
+
+//统一回调函数
+let callback = function(req, res, err, data){
+    if(err){
+        res.json({success: false, error: err});
+    }
+    else{
+        //res.send({success: true, data: data});
+        res.json({success:true, data: data});
+    }
+};
+
+module.exports = {
+
+    restfulTestResponse: function (req, res) {
+        console.log('you are calling restfulTestResponse.');
+        callback(req, res, null, 'good!');
+    },
+
+    restfulTestResponsePost: function (req, res) {
+        console.log('you are calling restfulTestResponse by POST way.');
+        callback(req, res, null, 'good!');
+    },
+
+    createExcelFilesInOneBookEx: function (req, res) {
+        //采用了优化策略
+        let params = JSON.parse(req.body.params),
+            rptPageRstArray = params.rptPageRstArray,
+            uuid = params.uuid
+        ;
+
+        // if (rpt_ids && rpt_ids.length > 0) {
+        //     getMultiRptsCommon(user_id, prj_id, rpt_ids, pageSize, orientation, customizeCfg, dftOption, JV.OUTPUT_TYPE_NORMAL, function (err, rptPageRstArray) {
+        //         if(err){
+        //             console.log('导出Excel错误(生成数据过程错误), userId: ' + user_id + ', prjId' + prj_id);
+        //             callback(req, res, '数据有误', null);
+        //         } else {
+        //             if ((rpt_bill_tpl_ids && rpt_bill_tpl_ids.length > 0) || (rpt_glj_tpl_ids && rpt_glj_tpl_ids.length > 0)) {
+        //                 getSummaryComboPages(user_id, prjIds, rpt_bill_tpl_ids, rpt_glj_tpl_ids, pageSize, orientation, customizeCfg, option, JV.OUTPUT_TYPE_NORMAL, function (err, rptSumPageRstArray) {
+        //                     rpt_xl_util.exportExcelInOneBook(rptPageRstArray.concat(rptSumPageRstArray), pageSize, rptName, function(uuidName){
+        //                         let fileRst = {uuid: uuidName, reportName: rptName};
+        //                         callback(req, res, err, fileRst);
+        //                     });
+        //                 });
+        //             } else {
+        //                 rpt_xl_util.exportExcelInOneBook(rptPageRstArray, pageSize, rptName, function(uuidName){
+        //                     let fileRst = {uuid: uuidName, reportName: rptName};
+        //                     callback(req, res, err, fileRst);
+        //                 });
+        //             }
+        //         }
+        //     });
+        // }
+    },
+
+    createExcelFilesEx: function (req, res) {
+        let params = JSON.parse(req.body.params),
+            pageSize = params.pageSize,
+            isOneSheet = params.isOneSheet,
+            rptPageRstArray = params.rptPageRstArray
+        ;
+        // if (rpt_ids && rpt_ids.length > 0) {
+        //     getMultiRptsCommon(user_id, prj_id, rpt_ids, pageSize, orientation, customizeCfg, dftOption, JV.OUTPUT_TYPE_NORMAL, function (err, rptPageRstArray) {
+        //         if (err) {
+        //             console.log('导出Excel错误(生成数据过程错误), userId: ' + user_id + ', prjId' + prj_id);
+        //             callback(req, res, '数据生成错误', null);
+        //         } else {
+        //             if ((rpt_bill_tpl_ids && rpt_bill_tpl_ids.length > 0) || (rpt_glj_tpl_ids && rpt_glj_tpl_ids.length > 0)) {
+        //                 getSummaryComboPages(user_id, prjIds, rpt_bill_tpl_ids, rpt_glj_tpl_ids, pageSize, orientation, customizeCfg, option, JV.OUTPUT_TYPE_NORMAL, function (err, rptSumPageRstArray) {
+        //                     let parallelFunctions = [];
+        //                     pri_Add_Parallel_Functions(parallelFunctions, rptPageRstArray, rpt_names, err);
+        //                     pri_Add_Parallel_Functions(parallelFunctions, rptSumPageRstArray, sum_rpt_names, err);
+        //                     pri_Run_Parallel_Functions(parallelFunctions);
+        //                 });
+        //             } else {
+        //                 let parallelFunctions = [];
+        //                 pri_Add_Parallel_Functions(parallelFunctions, rptPageRstArray, rpt_names, err);
+        //                 pri_Run_Parallel_Functions(parallelFunctions);
+        //             }
+        //         }
+        //     });
+        // } else if ((rpt_bill_tpl_ids && rpt_bill_tpl_ids.length > 0) || (rpt_glj_tpl_ids && rpt_glj_tpl_ids.length > 0)) {
+        //     getSummaryComboPages(user_id, prjIds, rpt_bill_tpl_ids, rpt_glj_tpl_ids, pageSize, orientation, customizeCfg, option, JV.OUTPUT_TYPE_NORMAL, function (err, rptSumPageRstArray) {
+        //         let parallelFunctions = [];
+        //         pri_Add_Parallel_Functions(parallelFunctions, rptSumPageRstArray, sum_rpt_names, err);
+        //         pri_Run_Parallel_Functions(parallelFunctions);
+        //     });
+        // }
+    },
+
+    createPdfFilesEx: function (req, res) {
+        let params = JSON.parse(req.body.params),
+            pageSize = params.pageSize,
+            rptPageRstArray = params.rptPageRstArray,
+            uuidArray = params.uuids
+        ;
+        // console.log('start!');
+        let msg = 'sent the export request!';
+        let err = null;
+        if (rptPageRstArray instanceof Array && rptPageRstArray.length > 0) {
+            for (let rIdx = 0; rIdx < rptPageRstArray.length; rIdx++) {
+                try {
+                    rpt_pdf_util.export_pdf_file(rptPageRstArray[rIdx], pageSize, uuidArray[rIdx], null); // 创建线程来实现build PDF,但无call back
+                } catch (ex) {
+                    if (err) {
+                        err = err + 'Has error on: ' + rIdx + ' ';
+                    } else {
+                        err = 'Has error on: ' + rIdx + ' ';
+                    }
+                }
+            }
+        }
+        callback(req, res, err, msg);
+    },
+
+    getFileByUUID: function (req, res) {
+        let uuid = req.params.uuid,
+            rptName = req.params.rptName,
+            suffix = "." + req.params.suffix
+        ;
+        // let user_id = req.session.sessionUser.id; //未来要校验user id
+        try {
+            res.setHeader('Content-Type', 'application/vnd.openxmlformats');
+            let rptNameURI = encodeURI(rptName);
+            res.setHeader("Content-Disposition", "attachment; filename=\"" + rptNameURI + suffix + "\"; filename*=utf-8''" + rptNameURI + suffix );
+            let filestream = fs.createReadStream(__dirname.slice(0, __dirname.length - 28) + '/tmp/' + uuid + suffix);
+            filestream.on('data', function(chunk) {
+                res.write(chunk);
+            });
+            filestream.on('end', function() {
+                res.end();
+            });
+        } catch (e) {
+            console.log(e);
+        }
+    },
+};

+ 4 - 0
modules/output/exportController.js

@@ -0,0 +1,4 @@
+/**
+ * Created by Tony on 2020/3/11.
+ */
+

+ 14 - 0
modules/output/jpc_value_define.js

@@ -0,0 +1,14 @@
+const fs = require('fs');
+
+function getValDefine() {
+    let tmpRst = null;
+    let data = fs.readFileSync(__dirname.slice(0, __dirname.length - 15) + '/public/web/rpt_value_define.js', 'utf8', 'r');
+    eval(data + ' ; tmpRst = JV;');
+    return tmpRst;
+};
+
+const VAL_DEF = getValDefine();
+
+//export default VAL_DEF;
+//export {VAL_DEF as default};
+module.exports = VAL_DEF;

+ 28 - 0
modules/output/rpt_font_util.js

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

+ 405 - 0
modules/output/rpt_pdf_util.js

@@ -0,0 +1,405 @@
+/**
+ * Created by chen on 2017/8/16.
+ */
+/**
+ * Created by zhang on 2017/8/14.
+ */
+
+const pdf = require('pdfkit');
+const PDF_SCALE = 0.75;
+const fs = require('fs');
+// const jpcCmnHelper = require('../rpt_component/helper/jpc_helper_common');
+const DPI = 96 * PDF_SCALE;
+const JV = require('./jpc_value_define');
+// const uuidV1 = require('uuid/v1');
+
+let fontUtil = require('./rpt_font_util');
+
+// 目前不支持下划线
+
+module.exports = {
+    export_pdf_file:export_pdf_file
+}
+
+function export_pdf_file (pageData, paperSize, fName, callback) {
+    let offsetX= 10;
+    let offsetY=10;
+    let doc = new pdf({autoFirstPage: false});
+    // let newName = '' + (new Date()).valueOf();
+    let newName = fName();
+    let stream = doc.pipe(fs.createWriteStream(__dirname.slice(0, __dirname.length - 21) + '/tmp/'+newName+'.pdf'));
+    let pageObj = pageData;
+    // doc.rect(5,5,1190,890).lineWidth(1).strokeColor('black').stroke();//边框
+    let paperSizeIdx = JV.PAGES_SIZE_STR.indexOf(paperSize);
+    let size = JV.PAGES_SIZE[paperSizeIdx];
+
+    function private_getIniPageMergeBorder(mergedBand) {
+        let rst = {};
+        rst[JV.PROP_LEFT] = mergedBand[JV.PROP_LEFT];
+        rst[JV.PROP_RIGHT] = mergedBand[JV.PROP_RIGHT];
+        rst[JV.PROP_TOP] = mergedBand[JV.PROP_TOP];
+        rst[JV.PROP_BOTTOM] = mergedBand[JV.PROP_BOTTOM];
+        rst[JV.PROP_STYLE] = mergedBand[JV.PROP_STYLE];
+        return rst;
+    }
+
+    let newPageMergeBand = private_getIniPageMergeBorder(pageObj[JV.BAND_PROP_MERGE_BAND]);
+    if (pageObj && pageObj.items.length > 0 ) {
+        for(let i=0;i<pageObj.items.length;i++){
+            if (pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][0] > pageData[JV.NODE_PAGE_INFO][JV.NODE_PAGE_SIZE][1]) {
+                doc.addPage({size:[size[1]*DPI,size[0]*DPI]});
+            } else {
+                doc.addPage({size:[size[0]*DPI,size[1]*DPI]});
+            }
+            let page = pageObj.items[i],
+                fonts = pageObj[JV.NODE_FONT_COLLECTION],
+                styles = pageObj[JV.NODE_STYLE_COLLECTION],
+                controls = pageObj[JV.NODE_CONTROL_COLLECTION];
+
+            if (page[JV.PROP_PAGE_MERGE_BORDER]) {
+                newPageMergeBand[JV.PROP_LEFT] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_LEFT];
+                newPageMergeBand[JV.PROP_RIGHT] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_RIGHT];
+                newPageMergeBand[JV.PROP_TOP] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_TOP];
+                newPageMergeBand[JV.PROP_BOTTOM] = page[JV.PROP_PAGE_MERGE_BORDER][JV.PROP_BOTTOM];
+            }
+            for (let j = 0; j < page.cells.length; j++) {
+                let cell = page.cells[j];
+                private_drawCell(cell, fonts, styles, controls, newPageMergeBand);
+            }
+        }
+    }
+    doc.end();
+    stream.on('finish',function () {
+        console.log(newName + ".pdf was written.");
+        if (callback) {
+            callback(newName);
+        }
+    });
+
+    function private_chkIfInMergedBand(mergedBand, cell) {
+        let rst = false;
+        if (mergedBand && cell) {
+            rst = mergedBand[JV.PROP_TOP] <= cell[JV.PROP_AREA][JV.PROP_TOP] && mergedBand[JV.PROP_BOTTOM] >= cell[JV.PROP_AREA][JV.PROP_BOTTOM] &&
+                mergedBand[JV.PROP_LEFT] <= cell[JV.PROP_AREA][JV.PROP_LEFT] && mergedBand[JV.PROP_RIGHT] >= cell[JV.PROP_AREA][JV.PROP_RIGHT];
+        }
+        return rst;
+    }
+
+    function private_drawCell(cell, fonts, styles, controls, mergedBand) {
+        doc.save();
+        // doc.translate(0.5,0.5); //跟H5的canvas不同,不需要这样切换
+        let style = styles[cell[JV.PROP_STYLE]];
+        if (style) {
+            let isNeedMergeBand = private_chkIfInMergedBand(mergedBand, cell);
+            private_drawLine(cell, doc, style, JV.PROP_TOP, [JV.PROP_LEFT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_TOP], mergedBand, styles, isNeedMergeBand);
+            private_drawLine(cell, doc, style, JV.PROP_RIGHT, [JV.PROP_RIGHT, JV.PROP_TOP],[JV.PROP_RIGHT, JV.PROP_BOTTOM], mergedBand, styles, isNeedMergeBand);
+            private_drawLine(cell, doc, style, JV.PROP_BOTTOM, [JV.PROP_RIGHT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_BOTTOM], mergedBand, styles, isNeedMergeBand);
+            private_drawLine(cell, doc, style, JV.PROP_LEFT, [JV.PROP_LEFT, JV.PROP_BOTTOM],[JV.PROP_LEFT, JV.PROP_TOP], mergedBand, styles, isNeedMergeBand);
+        }
+        private_drawCellText(cell, fonts, controls);
+        doc.restore();
+
+    }
+
+    function private_drawLine(cell, doc, style, styleBorderDest, startP, destP, mergedBand, styles, isNeedMergeBand) {
+        //doc.beginPath();
+        let destStyle = style;
+        if (mergedBand) {
+            if (isNeedMergeBand && parseFloat(mergedBand[styleBorderDest]) === parseFloat(cell[JV.PROP_AREA][styleBorderDest])) {
+                destStyle = styles[mergedBand[JV.PROP_STYLE][JV.PROP_ID]];
+            }
+        }
+        // doc.moveTo(cell[JV.PROP_AREA][startP[0]] + offsetX, cell[JV.PROP_AREA][startP[1]] + offsetY);
+        doc.moveTo( (cell[JV.PROP_AREA][startP[0]] + offsetX) * PDF_SCALE, (cell[JV.PROP_AREA][startP[1]] + offsetY) * PDF_SCALE);
+        if (destStyle[styleBorderDest] && parseFloat(destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT]) !== 0) {
+            doc.lineWidth(1.0 * destStyle[styleBorderDest][JV.PROP_LINE_WEIGHT]);
+            // doc.lineTo(cell[JV.PROP_AREA][destP[0]] + offsetX, cell[JV.PROP_AREA][destP[1]] + offsetY);
+            doc.lineTo((cell[JV.PROP_AREA][destP[0]] + offsetX) * PDF_SCALE, (cell[JV.PROP_AREA][destP[1]] + offsetY) * PDF_SCALE);
+            doc.strokeColor(destStyle[styleBorderDest][JV.PROP_COLOR]);
+        }
+        doc.stroke();
+    }
+    function private_drawCellText(cell, fonts, controls) {
+        if (cell[JV.PROP_VALUE]) {
+            let values = ("" + cell[JV.PROP_VALUE]).split('|');
+            // let font = fonts[cell[JV.PROP_FONT]];
+            let font = null;
+            if (typeof cell[JV.PROP_FONT] === "string") {
+                font = fonts[cell[JV.PROP_FONT]];
+            } else {
+                font = cell[JV.PROP_FONT];
+            }
+            // let control = controls[cell[JV.PROP_CONTROL]];
+            let control = null;
+            if (typeof cell[JV.PROP_CONTROL] === "string") {
+                control = controls[cell[JV.PROP_CONTROL]];
+            } else {
+                control = cell[JV.PROP_CONTROL];
+            }
+            let height = cell[JV.PROP_AREA][JV.PROP_BOTTOM] - cell[JV.PROP_AREA][JV.PROP_TOP];
+            let area = [cell[JV.PROP_AREA][JV.PROP_LEFT] + offsetX, cell[JV.PROP_AREA][JV.PROP_TOP] + offsetY, cell[JV.PROP_AREA][JV.PROP_RIGHT] + offsetX, cell[JV.PROP_AREA][JV.PROP_BOTTOM] + offsetY];
+            let ah = height;
+            let restTopH = 0, restBottomH = 0;
+            if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_CLOSE_OUTPUT]] === 'T') {
+                ah = (parseFloat(font[JV.FONT_PROPS[1]]) + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM]) * values.length;
+                let restH = height - ah;
+                if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === 'center') {
+                    restTopH = restH / 2;
+                    restBottomH = restH / 2;
+                } else if (control[JV.CONTROL_PROPS[JV.CONTROL_PROP_IDX_VERTICAL]] === 'bottom') {
+                    restBottomH = restH;
+                } else {
+                    restTopH = restH;
+                }
+            }
+            let spaceIdxArr = [];
+            for (let i = 0; i < values.length; i++) {
+                // area[JV.IDX_TOP] = cell[JV.PROP_AREA][JV.PROP_TOP] + i * (height / values.length) + offsetY;
+                // area[JV.IDX_BOTTOM] = cell[JV.PROP_AREA][JV.PROP_TOP] + (i + 1) * (height / values.length) + offsetY;
+                area[JV.IDX_TOP] = cell[JV.PROP_AREA][JV.PROP_TOP] + i * (ah / values.length) + offsetY + restTopH;
+                area[JV.IDX_BOTTOM] = cell[JV.PROP_AREA][JV.PROP_TOP] + (i + 1) * (ah / values.length) + offsetY + restBottomH;
+                if (values[i] === null || values[i] === undefined || values[i] === 'null') {
+                    values[i] = "";
+                }
+                // 因pdfkit输出空格只有一半宽度,需要额外加空格补上 -----------------------------
+                if (typeof(values[i]) === "string") {
+                    for (let j = 0; j < values[i].length; j++) {
+                        if (values[i][j] === ' ') spaceIdxArr.push(j);
+                    }
+                }
+                for (let j = spaceIdxArr.length - 1; j >= 0; j--) {
+                    values[i] = values[i].slice(0, spaceIdxArr[j]) + ' ' + values[i].slice(spaceIdxArr[j]);
+                }
+                // -----------------------------
+                private_drawText(values[i], area, font, control);
+                spaceIdxArr = [];
+            }
+        }
+    }
+
+    function private_drawText(val, area, font, control) {
+        let dftFontHeight = 12;
+        let output = [];
+        let fontFile = __dirname + '/pdf_base_files/simkai.ttf';
+        if (font) {
+            dftFontHeight = 1 * font[JV.FONT_PROPS[1]];
+            fontFile = __dirname + '/pdf_base_files/' + fontUtil.getActualFont(font[JV.FONT_PROPS[0]], (font[JV.FONT_PROPS[3]] === 'T'), (font[JV.FONT_PROPS[4]] === 'T')) + '.ttf';
+            doc.fontSize(dftFontHeight);
+        }
+        doc.font(fontFile);
+        let options={};
+        let inner_setupControl = function (inArea, inFontHeight, inOutput) {
+            if (control) {
+                private_setupAreaH(inArea, control.Horizon, font.FontAngle, inFontHeight, inOutput, options);
+                private_setupAreaV(inArea, control.Vertical, font.FontAngle, inFontHeight, inOutput);
+            } else {
+                private_setupAreaH(inArea, "left", parseInt(font.FontAngle), inFontHeight, inOutput, options);
+                private_setupAreaV(inArea, "bottom", parseInt(font.FontAngle), inFontHeight, inOutput);
+            }
+        };
+        inner_setupControl(area, dftFontHeight, output);
+        let validAreaTxtWidth = area[JV.IDX_RIGHT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] - area[JV.IDX_LEFT] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT];
+        let validTxtLines = Math.floor((area[JV.IDX_BOTTOM] - area[JV.IDX_TOP]) / (dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + 4));
+        if (parseInt(font.FontAngle) !== 0) {
+            validAreaTxtWidth = area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] - area[JV.IDX_TOP] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+            validTxtLines = Math.floor((area[JV.IDX_RIGHT] - area[JV.IDX_LEFT]) / (dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_RIGHT] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT] + 4));
+        }
+
+        function private_drawUnderline(underLineVal, underLineArea) {
+            //A. 暂不支持角度; B. PDF输出时,坐标没有translate
+            let ctx = doc;
+            //1. 计算下划线的相关坐标
+            let width = ctx.widthOfString(underLineVal);
+            if (width > underLineArea[JV.IDX_RIGHT] - underLineArea[JV.IDX_LEFT]) {
+                width = underLineArea[JV.IDX_RIGHT] - underLineArea[JV.IDX_LEFT];
+            }
+            let height = dftFontHeight;
+            let startX = underLineArea[JV.IDX_LEFT], startY = underLineArea[JV.IDX_TOP], endX = underLineArea[JV.IDX_RIGHT], endY = underLineArea[JV.IDX_BOTTOM];
+            // let startX = 0, startY = 0, endX = width, endY = startY;
+            if (control.Horizon === "left") {
+                startX = Math.round(underLineArea[JV.IDX_LEFT] + JV.OUTPUT_OFFSET[JV.IDX_LEFT]);
+            } else if (control.Horizon === "right") {
+                startX = Math.round(underLineArea[JV.IDX_RIGHT] - width - JV.OUTPUT_OFFSET[JV.IDX_RIGHT]);
+            } else {
+                startX = Math.round( underLineArea[JV.IDX_LEFT] + (underLineArea[JV.IDX_RIGHT] - underLineArea[JV.IDX_LEFT] - width) / 2);
+            }
+            endX = Math.round(startX + width);
+
+            if (control.Vertical === "top") {
+                startY = Math.round(underLineArea[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.IDX_BOTTOM] + height);
+            } else if (control.Vertical === "bottom") {
+                startY = Math.round(underLineArea[JV.IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.IDX_BOTTOM]);
+            } else {
+                startY = Math.round( underLineArea[JV.IDX_TOP] + (underLineArea[JV.IDX_BOTTOM] - underLineArea[JV.IDX_TOP] + height) / 2) + JV.OUTPUT_OFFSET[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.IDX_BOTTOM];
+            }
+            endY = Math.round(startY);
+            //2. 画线
+            // ctx.save();
+            if ( output[1] !== Math.round(output[1])) {
+                ctx.translate(0,0.5);
+            }
+            // ctx.beginPath();
+            // ctx.moveTo(startX, startY);
+            ctx.moveTo(startX * PDF_SCALE, startY * PDF_SCALE);
+            ctx.lineWidth(1);
+            ctx.strokeStyle = "BLACK";
+            // ctx.lineTo(endX, endY);
+            ctx.lineTo(endX * PDF_SCALE, endY * PDF_SCALE);
+            ctx.stroke();
+            // ctx.restore();
+        }
+
+        let rotateOptions;
+        // if (font[JV.FONT_PROPS[5]] === 'T' && parseInt(font.FontAngle) === 0) {
+        //     private_drawUnderline(val);
+        // }
+        if (parseInt(font.FontAngle) !== 0) {
+            if (control){
+                rotateOptions = private_setupAreaRotateOption(area,validAreaTxtWidth,control.Vertical,dftFontHeight, output);
+            } else {
+                rotateOptions = private_setupAreaRotateOption(area,validAreaTxtWidth,"bottom",dftFontHeight, output);
+            }
+            doc.rotate(font.FontAngle,rotateOptions);
+        }
+        if (validAreaTxtWidth >= doc.widthOfString(val) ||
+            (control && control.Shrink !== 'T' && validTxtLines < private_splitString(val, validAreaTxtWidth, doc)) ) {
+            options.width = validAreaTxtWidth * PDF_SCALE;
+            options.height = dftFontHeight * PDF_SCALE;
+            doc.fontSize(dftFontHeight);
+            if (font[JV.FONT_PROPS[5]] === 'T' && parseInt(font.FontAngle) === 0) {
+                private_drawUnderline(val, area);
+            }
+            doc.fontSize(dftFontHeight * PDF_SCALE);
+            doc.text(val,output[0] * PDF_SCALE, output[1] * PDF_SCALE, options);
+            doc.font(__dirname + '/pdf_base_files/simhei_bold_italic.ttf');
+        } else {
+            while (true) {
+                //*/
+                let lines = Math.floor((area[JV.IDX_BOTTOM] - area[JV.IDX_TOP]) / (dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + 4));
+                lines = (lines === 0 || (control.Shrink === 'T' && control.ShrinkFirst === 'T'))?1:lines;
+                let actLines = private_splitString(val, validAreaTxtWidth, doc);
+                if (actLines.length > lines && dftFontHeight >= 6) {
+                    dftFontHeight--;
+                    doc.fontSize(dftFontHeight);
+                    options.width = validAreaTxtWidth * PDF_SCALE;
+                    options.height = dftFontHeight * PDF_SCALE;
+                    // doc.text(val,output[0], output[1], options);
+                } else {
+                    let aH = dftFontHeight + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP] + 4;
+                    if ((aH * actLines.length) < (area[JV.IDX_BOTTOM] - area[JV.IDX_TOP]) && (control && control.Vertical !== 'top')) {
+                        if (control.Vertical === 'bottom') {
+                            area[JV.IDX_TOP] = area[JV.IDX_BOTTOM] - (aH * actLines.length);
+                        } else {
+                            area[JV.IDX_TOP] = (area[JV.IDX_TOP] + area[JV.IDX_BOTTOM]) / 2 - (aH * actLines.length) / 2
+                            area[JV.IDX_BOTTOM] = area[JV.IDX_TOP] + (aH * actLines.length);
+                        }
+                    }
+                    let newArea = [], baseTop = area[JV.IDX_TOP];
+                    for (let ai = 0; ai < area.length; ai++) {
+                        newArea[ai] = area[ai];
+                    }
+                    options.width = validAreaTxtWidth * PDF_SCALE;
+                    options.height = dftFontHeight * PDF_SCALE;
+                    for (let lIdx = 0; lIdx < actLines.length; lIdx++) {
+                        newArea[JV.IDX_TOP] = Math.round(aH * lIdx + baseTop);
+                        newArea[JV.IDX_BOTTOM] = Math.round(aH * (lIdx + 1) + baseTop);
+                        inner_setupControl(newArea, dftFontHeight, output);
+                        doc.fontSize(dftFontHeight);
+                        if (font[JV.FONT_PROPS[5]] === 'T' && parseInt(font.FontAngle) === 0) {
+                            private_drawUnderline(actLines[lIdx], newArea);
+                        }
+                        doc.fontSize(dftFontHeight * PDF_SCALE);
+                        doc.text(actLines[lIdx], output[0] * PDF_SCALE, output[1] * PDF_SCALE, options);
+                    }
+                    break;
+                }
+                /*/
+                dftFontHeight--;
+                doc.fontSize(dftFontHeight);
+                if (validAreaTxtWidth >= doc.widthOfString(val) || dftFontHeight < 6) {
+                    options.width = validAreaTxtWidth;
+                    options.height = dftFontHeight;
+                    doc.text(val,output[0], output[1], options);
+                    doc.font(__dirname + '/pdf_base_files/simhei_bold_italic.ttf');
+                    break;
+                }
+                //*/
+            }
+        }
+        // doc.text(val,output[0], output[1], options);
+        doc.font(__dirname + '/pdf_base_files/simhei_bold_italic.ttf');
+        // doc.restore();
+    }
+
+    function private_setupAreaH(area, type, fontAngle, dftFontHeight, outputPoint,options) {
+        let lType = type;
+        if (type !== "left" && type !== "right" && type !== "center") lType = "left";
+        options.align=lType;
+        outputPoint[0]=1 * area[JV.IDX_LEFT]+ JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
+    }
+
+    function private_setupAreaV(area, type, fontAngle, dftFontHeight, outputPoint) {
+        let lType = type;
+        if (type !== "top" && type !== "bottom" && type !== "center") lType = "top";
+        switch (lType) {
+            case "top":
+                outputPoint[1] = 1 * area[JV.IDX_TOP] + JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                break;
+            case "bottom":
+                outputPoint[1] = 1 * area[JV.IDX_BOTTOM] - JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM]-dftFontHeight;
+                break;
+            case "center":
+                outputPoint[1] = 1 * area[JV.IDX_TOP] + (1 * area[JV.IDX_BOTTOM]-1 * area[JV.IDX_TOP]- dftFontHeight) / 2;
+                break;
+        }
+    }
+
+    function private_splitString(strVal, areaWidth, doc) {
+        let rst = [];
+        if (strVal) {
+            let preSIdx = 0, txtWidth = 0;
+            let currentW = 0;
+            let chnW = doc.widthOfString('一'), otherW = doc.widthOfString('_');
+            for (let sIdx = 0; sIdx < strVal.length; sIdx++) {
+                currentW = (strVal.charCodeAt(sIdx) > 127)?chnW:otherW;
+                txtWidth += currentW;
+                if (txtWidth > areaWidth) {
+                    if (preSIdx < sIdx) {
+                        rst.push(strVal.substr(preSIdx, sIdx - preSIdx));
+                        preSIdx = sIdx;
+                    } else {
+                        rst.push(strVal.substr(preSIdx, 1));
+                        preSIdx = sIdx + 1;
+                    }
+                    txtWidth = currentW;
+                }
+                if (sIdx === strVal.length - 1) {
+                    rst.push(strVal.substr(preSIdx, strVal.length - preSIdx));
+                }
+            }
+        }
+        if (rst.length === 0) rst.push(''); //什么都没有,也得整个空串
+        return rst;
+    }
+
+    function private_setupAreaRotateOption(area,w, type="top",dftFontHeight,outputPoint){
+        let x = (area[JV.IDX_RIGHT] - area[JV.IDX_LEFT])/2+area[JV.IDX_LEFT];
+        let y =(area[JV.IDX_BOTTOM] - area[JV.IDX_TOP])/2+ area[JV.IDX_TOP];
+        let rotateOptions = {origin:[x,y]};
+        let h = area[JV.IDX_RIGHT] - area[JV.IDX_LEFT];
+        outputPoint[0]=x-w/2+JV.OUTPUT_OFFSET[JV.OFFSET_IDX_LEFT];
+        switch (type) {
+            case "top":
+                outputPoint[1] = y- h/2+ JV.OUTPUT_OFFSET[JV.OFFSET_IDX_TOP];
+                break;
+            case "bottom":
+                outputPoint[1] = y+ h/2-JV.OUTPUT_OFFSET[JV.OFFSET_IDX_BOTTOM]-dftFontHeight;
+                break;
+            case "center":
+                outputPoint[1] = y+dftFontHeight/2;
+                break;
+        }
+        return rotateOptions;
+    }
+
+}

+ 53 - 0
modules/output/rpt_tmp_file_sweep.js

@@ -0,0 +1,53 @@
+/**
+ * Created by Tony on 2018/7/27.
+ */
+
+let fs = require('fs');
+let cron = require('node-schedule');
+let remove_file_types = [".xlsx", ".pdf", ".jsp"];
+function chkIsRemoveType(file) {
+    let rst = false;
+    for (let fType of remove_file_types) {
+        if (file.indexOf(fType) === (file.length - fType.length)) {
+            rst = true;
+            break;
+        }
+    }
+    return rst;
+}
+let jobObj = {
+    createJob: function (rule, rootPath) {
+        let localRule = rule;
+        if (!localRule) {
+            // setup schedule rule
+            localRule = new cron.RecurrenceRule();
+            // 3:15AM, everyday
+            localRule.dayOfWeek = [1,2,3,4,5,6,0];
+            localRule.hour = 3;
+            localRule.minute = 15;
+        }
+        cron.scheduleJob(localRule, function(){
+            let path = rootPath;
+            fs.exists(path, function (exists) {
+                if (exists) {
+                    fs.readdir(path, function (err, files) {
+                        let currentTime = (new Date()).valueOf();
+                        let timeGap = currentTime - (1000 * 60 * 60 * 24 * 0.5); //half day before
+                        files.forEach(function(file,index){
+                            if (chkIsRemoveType(file)) {
+                                let curPath = path + "/" + file;
+                                fs.stat(curPath,function(err,data){
+                                    if (timeGap > data.mtime) {
+                                        fs.unlink(curPath);
+                                    }
+                                });
+                            }
+                        });
+                    })
+                }
+            })
+        });
+    }
+};
+
+export default jobObj;

+ 16 - 3
package.json

@@ -10,12 +10,25 @@
     "type": "git",
     "url": "http://192.168.1.41:3000/tonykang/CommonReportExport.git"
   },
-  "dependencies": {
+  "devDependencies": {
+    "body-parser": "^1.13.2",
     "express": "^4.13.1",
+    "async": "^2.1.5",
+    "cookie-parser": "~1.4.3",
+    "debug": "~2.6.0",
+    "ejs": "~2.5.5",
+    "express-session": "^1.15.1",
+    "request": "^2.79.0",
+    "tape": "^4.6.3",
+    "nodemon": "^1.11.0",
+    "lodash": "^3.10.1",
+    "glob": "~4.0.5"
+  },
+  "dependencies": {
     "jszip": "^3.1.3",
     "node-schedule": "^1.3.0",
+    "uuid": "^3.1.0",
     "pdfkit": "^0.8.2"
   },
-  "author": "Tony Kang",
-  "license": "ISC"
+  "author": "Tony Kang"
 }

+ 35 - 0
public/fsUtil.js

@@ -0,0 +1,35 @@
+/**
+ * Created by Tony on 2017/4/10.
+ */
+
+let fs = require('fs');
+
+module.exports = {
+    writeArrayToFile: function(arr, filePath) {
+        if (arr && filePath && Array.isArray(arr)) {
+            let chunks = [], len = 0;
+            for (let i = 0; i < arr.length; i++) {
+                let buffer = new Buffer(arr[i]);
+                chunks.push(buffer);
+                len += buffer.length;
+                //
+            }
+            let resultBuffer = new Buffer(len);
+            for(let i=0,size=chunks.length,pos=0;i<size;i++){
+                chunks[i].copy(resultBuffer,pos);
+                pos += chunks[i].length;
+            }
+            fs.writeFile(filePath, resultBuffer, function(err){
+                if(err) throw err;
+                //console.log('Write file: ' + filePath + ' ok!');
+            });
+        }
+    },
+    writeObjToFile: function(obj, filePath) {
+        if (obj) {
+            let arr = [];
+            arr.push(JSON.stringify(obj));
+            this.writeArrayToFile(arr, filePath);
+        }
+    }
+};

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 284 - 0
public/stringUtil.js


+ 330 - 0
public/web/rpt_value_define.js

@@ -0,0 +1,330 @@
+/**
+ * Created by Tony on 2017/6/7.
+ */
+const JV = {
+    NODE_CROSS_INFO: "交叉表_信息",
+    NODE_CROSS_ROW: "交叉行",
+    NODE_CROSS_COL: "交叉列",
+    NODE_CROSS_CONTENT: "交叉数据",
+    NODE_CROSS_ROW_SUM: "交叉行合计",
+    NODE_CROSS_COL_SUM: "交叉列合计",
+    NODE_CROSS_ROW_EXT: "交叉行拓展",
+    NODE_CROSS_ROW_SUM_EXT: "交叉行拓展合计",
+    NODE_CROSS_ROW_AD_HOC: "交叉行AD_HOC",
+    NODE_FIELD_MAP: "指标_数据_映射",
+    NODE_DISCRETE_FIELDS: "离散指标_集合",
+    NODE_NO_MAPPING_FIELDS: "无映射离散指标_集合",
+    NODE_DISCRETE_PARAMS: "离散参数_集合",
+    NODE_MASTER_FIELDS: "主数据指标_集合",
+    NODE_MASTER_FIELDS_EX: "主数据指标_拓展集合",
+    NODE_DETAIL_FIELDS: "从数据指标_集合",
+    NODE_DETAIL_FIELDS_EX: "从数据指标_拓展集合",
+    NODE_BAND_COLLECTION: "布局框_集合",
+    NODE_FORMULAS: "计算式_集合",
+    NODE_EVENTS: "事件_集合",
+    NODE_DISCRETE_INFO: "离散信息",
+    NODE_BILL_INFO: "账单式表_信息",
+    NODE_BILL_CONTENT : "账单式表_数据",
+    NODE_FLOW_INFO: "流水式表_信息",
+    NODE_FLOW_INFO_EX: "流水式表_拓展信息",
+    NODE_FLOW_GROUP: "流水式表_分组信息",
+    NODE_FLOW_SEG_SUM: "流水式表_段统计信息",
+    NODE_FLOW_PAGE_SUM: "流水式表_页统计信息",
+    NODE_FLOW_COLUMN : "流水式表_列",
+    NODE_FLOW_CONTENT : "流水式表_数据",
+    PROP_MULTI_COLUMN: "多列显示数量",
+    PROP_FLOW_EX_DISPLAY_MODE: "流水拓展显示模式",
+    DISPLAY_MODE_INDEPENDENT: "单独模式",
+    DISPLAY_MODE_FOLLOW: "紧随模式",
+
+    NODE_MAIN_INFO: "主信息",
+    NODE_MAIN_INFO_RPT_NAME: "报表名称",
+    NODE_PAGE_INFO: "打印页面_信息",
+    NODE_PAGE_SIZE: "纸张宽高",
+    NODE_MARGINS: "页边距",
+    NODE_RPT_BIZ_TYPE: "报表业务类型",
+    NODE_BIZ_TYPE_AUDI: "审核类型",
+    NODE_BIZ_TYPE_SUM: "汇总类型",
+    NODE_BIZ_TYPE_DETAIL: "明细类型",
+
+    NODE_MAP_DATA_HANDLE_INFO: "映射数据预处理",
+    PROP_DATA_KEY: "映射数据对象",
+    PROP_PARENT_DATA_KEY: "父映射数据对象",
+    PROP_PARENT_CHILD_SORT_KEY: "父子排序键",
+    PROP_PARENT_SORT_KEYS: "父排序键值集",
+    PROP_CHILD_SORT_KEYS: "子排序键值集",
+    PROP_OTHER_SUB_SORT: "其他子排序",
+    PROP_OTHER_SUB_FILTER: "其他子过滤",
+    PROP_HANDLE_TYPE: "预处理类型",
+    PROP_FILTER_KEYS: "过滤键值集",
+    PROP_FILTER_TOP_BILLS_NODES: "清单顶节点集",
+    PROP_FILTER_OTHER_BILLS_NODES: "其他清单节点集",
+    PROP_FILTER_COMPARE_OBJ: "compareObjKey",
+    PROP_FILTER_COMPARE_OBJ_KEY: "compareObjIdKey",
+    PROP_FILTER_COMPARE_VAL: "compareValue",
+    PROP_FILTER_CONDITION: "判断条件",
+    PROP_HANDLE_TYPE_FILTER: "过滤",
+    PROP_HANDLE_TYPE_SUM: "合计",
+    PROP_HANDLE_TYPE_SORT: "排序",
+    PROP_HANDLE_TYPE_ADD_DUMMY: "增加Dummy数据",
+    PROP_HANDLE_TYPE_ADJUST: "数据调整",
+    PROP_HANDLE_TYPE_BILLS_DATA_MOVE: "量材数据转移",
+    PROP_HANDLE_TYPE_COMPONENT_MOVE: "组成物数据转移",
+    PROP_HANDLE_TYPE_COMPONENT_REPLACEMENT: "组成物替换",
+    PROP_HANDLE_TYPE_PRECISION: "合计精度",
+
+    PROP_ADJUST_COLLECTION: "数据调整集",
+    PROP_ADJUST_ACTION: "action",
+    PROP_ADJUST_ACTION_VAL: "actionValue",
+    PROP_DUMMY_COLLECTION: "Dummy数据集",
+    PROP_DUMMY_VAL: "Dummy数据对象值",
+    PROP_FREQUENCY: "频率",
+    PROP_GRP_KEYS: "GrpKeyIds",
+    PROP_SORT_TYPE: "排序方式",
+    PROP_SORT_TYPE_SELF_DEFINE_LOGIC: "自定义逻辑",
+    PROP_SORT_KEYS: "排序键值集",
+    PROP_SUM_GROUP_KEYS: "分组键值集",
+    PROP_SUM_SUM_KEYS: "统计键值集",
+    PROP_SUM_CALC_AHEAD: "统计前计算",
+    PROP_SUM_CACL_TYPE: "计算类型",
+    PROP_FIELD_EXP_MAP: "mapExpression",
+    PROP_PRECISION: "Precision",
+    PROP_FIXED_PRECISION_AMT: "fixedPrecisionNum",
+    PROP_FIELD_EXP_FIXED_MAP: "fixedMapExpression",
+    PROP_FIELD_EXP_FLEXIBLE_MAP: "flexibleMapExpression",
+    PROP_FLEXIBLE_REF_FILED_ID: "flexibleRefFieldID",
+
+    NODE_FONT_COLLECTION: "font_collection",
+    NODE_STYLE_COLLECTION: "style_collection",
+    NODE_CONTROL_COLLECTION: "control_collection",
+
+    PROP_ID: "ID",
+    PROP_AD_HOC_DATA: "data_field",
+    PROP_CMN_HEIGHT: "CommonHeight",
+    PROP_CMN_WIDTH: "CommonWidth",
+    PROP_BAND_NAME: "BandName",
+    PROP_BAND_NORMAL_ONLY: "normalOnly",
+    PROP_BAND_EX_ONLY: "exOnly",
+    PROP_BAND_EX_JOIN_AFTER: "isJoinAfter",
+
+    PROP_UNITS: "单位",
+    PROP_PAGE_SIZE: "页规格",
+    PROP_ORIENTATION: "方向",
+    PROP_LEFT: "Left",
+    PROP_RIGHT: "Right",
+    PROP_TOP: "Top",
+    PROP_BOTTOM: "Bottom",
+    PROP_DATA_TYPE: "DataType",
+    PROP_NAME: "Name",
+    PROP_DFT_VALUE: "Default_Value",
+    PROP_EXPRESSION: "expression",
+    PROP_RUN_TYPE: "run_type",
+    PROP_BORDER_STYLE: "border_style",
+    PROP_POSITION: "Position",
+    PROP_HIDDEN: "Hidden",
+    PROP_IS_SERIAL: "isSerial",
+    PROP_IS_MERGE: "isMerge",
+    PROP_COMBINE_TYPE: "combineType",
+    PROP_IS_AUTO_HEIGHT: "isAutoHeight",
+    PROP_FONT: "font",
+    PROP_CONTROL: "control",
+    PROP_STYLE: "style",
+    PROP_VALUE: "Value",
+    PROP_LABEL: "Label",
+    PROP_AREA: "area",
+    PROP_DISCRETE_FIELDS: "discrete_field_s",
+    PROP_FLOW_FIELDS: "flow_field_s",
+    PROP_BILL_FIELDS: "bill_field_s",
+    PROP_CROSS_FIELDS: "cross_field_s",
+    PROP_CROSS_DISPLAY_ORDER: "cross_display_order",
+    PROP_GROUP_FIELDS: "group_field_s", //用来分组的指标(如按清单、定额etc...)
+    PROP_GROUP_LINES: "group_lines",    //显示分组行,因分组的特殊性,分组的数据当成流水数据一样(行高相同),group_lines里的每一条数据占用流水的一整行,里面再细分(指标/text)
+    PROP_GROUP_SUM_KEYS: "SumKey_S",
+    PROP_SUM_KEY: "SumKey",
+    PROP_SUM_FIELDS: "sum_field_s",
+    PROP_TEXTS: "text_s",
+    PROP_TEXT: "text",
+    PROP_PARAMS: "param_s",
+    PROP_FIELD_ID: "FieldID",
+    PROP_PARAM_ID: "ParamID",
+    PROP_PREFIX: "Prefix",
+    PROP_SUFFIX: "Suffix",
+    PROP_FORMAT: "Format",
+
+    PROP_SHOW_ZERO: "ShowZero",
+    PROP_EXTENSION_TYPE: "ExtType",
+
+    PROP_CALCULATION: "CalculationType",
+    PROP_H_CALCULATION: "H_CalculationType",
+    PROP_V_CALCULATION: "V_CalculationType",
+    PROP_FIT_AREA: "isFitArea",
+
+    IDX_LEFT: 0,
+    IDX_TOP: 1,
+    IDX_RIGHT: 2,
+    IDX_BOTTOM: 3,
+
+    BAND_PROP_NAME: "Name",
+    BAND_PROP_MERGE_BAND: "MergeBand",
+    BAND_PROP_STYLE: "style",
+    BAND_PROP_CONTROL: "control",
+    BAND_PROP_HEIGHT: "Height",
+    BAND_PROP_WIDTH: "Width",
+    BAND_PROP_DISPLAY_TYPE: "DisplayType",
+    BAND_PROP_ALIGNMENT: "Alignment",
+    BAND_PROP_MERGE_BORDER: "MergeBorder",
+    BAND_PROP_SUB_BANDS: "band_s",
+
+    MEASUREMENT: {
+        PIXEL:["像素点", "象素点", "PIXEL"],
+        CM: ["厘米", "CM"],
+        INCH: ["英寸","INCH"]
+    },
+
+    PROP_IS_ID: "isID",
+    PROP_ID_SEQ: "IDSeq",
+
+    TAB_FIELD_PROP_SORT: "Sort",
+    TAB_FIELD_PROP_SORT_VAL_NOSORT: "no_sort",
+    TAB_FIELD_PROP_SORT_VAL_ASC: "ascend",
+    TAB_FIELD_PROP_SORT_VAL_DESC: "descend",
+
+    DATA_DISCRETE_DATA: "discrete_data",
+    DATA_MASTER_DATA: "master_data",
+    DATA_DETAIL_DATA: "detail_data",
+    DATA_MASTER_DATA_EX: "master_data_ex",
+    DATA_DETAIL_DATA_EX: "detail_data_ex",
+
+    BLANK_FIELD_INDEX: -10,
+    BLANK_VALUE_INDEX: -100,
+    BLANK_PAGE_VALUE_INDEX: -200,
+
+    PROP_SEG_GRP_IDX: "segGrpRecStartIdx",
+    PROP_PRE_ADD_GRP_REC_INFO: "preAddPageGrpInfo",
+    PROP_INSERTED_GRP_REC: "insertedGrpRecAmt",
+    PROP_GRP_LINES: "group_lines_amt",
+
+    RUN_TYPE_BEFORE_ANALYZING: "before_analyzing",
+    RUN_TYPE_BEFORE_PAGING: "before_paging",
+    RUN_TYPE_BEFORE_OUTPUT: "before_output",
+    RUN_TYPE_BEFORE_COMBINE: "before_combine",
+    RUN_TYPE_AFTER_COMBINE: "after_combine",
+
+    RUN_TYPE_BEFORE_GROUP_TEXT_OUT: "before_group_text_output",
+
+    PAGE_STATUS: ["EveryPage","FirstPage", "LastPage", "SegmentStart", "SegmentEnd", "Group", "CrossRowEnd", "CrossColEnd"],
+
+    CONTROL_PROPS: ["Shrink", "ShowZero", "Horizon", "Vertical", "Wrap", "VerticalForExcel", "ShrinkFirst", "CloseOutput"],
+    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,
+    CONTROL_PROP_IDX_SHRINK_FIRST: 6,
+    CONTROL_PROP_IDX_CLOSE_OUTPUT: 7,
+    BORDER_STYLE_PROPS: ["LineWeight", "DashStyle", "Color"],
+    PROP_LINE_WEIGHT: "LineWeight",
+    PROP_DASH_STYLE: "DashStyle",
+    PROP_COLOR: "Color",
+    FONT_PROPS: ["Name", "FontHeight", "FontColor", "FontBold", "FontItalic", "FontUnderline", "FontStrikeOut", "FontAngle"],
+    FONT_PROP_IDX_NAME: 0,
+    FONT_PROP_IDX_HEIGHT: 1,
+    FONT_PROP_IDX_COLOR: 2,
+    FONT_PROP_IDX_BOLD: 3,
+    FONT_PROP_IDX_ITALIC: 4,
+    FONT_PROP_IDX_UNDERLINE: 5,
+    FONT_PROP_IDX_STRIKEOUT: 6,
+    FONT_PROP_IDX_ANGLE: 7,
+
+    STATUS_NORMAL: 0,
+    STATUS_REPORT_START: 1,
+    STATUS_REPORT_END: 2,
+    STATUS_SEGMENT_START: 3,
+    STATUS_SEGMENT_END: 4,
+    STATUS_GROUP: 5,
+    STATUS_CROSS_ROW_END: 6,
+    STATUS_CROSS_COL_END: 7,
+
+    LAYOUT: ["Top","Bottom", "Left", "Right", "Fulfill"],
+    LAYOUT_TOP: 0,
+    LAYOUT_BOTTOM: 1,
+    LAYOUT_LEFT: 2,
+    LAYOUT_RIGHT: 3,
+    LAYOUT_FULFILL: 4,
+
+    OUTPUT_ALIGN: {
+        H: ["left", "center", "right"],
+        V: ["top", "center", "bottom"]
+    },
+    H_ALIGN_IDX_LEFT : 0,
+    H_ALIGN_IDX_CENTER : 1,
+    H_ALIGN_IDX_RIGHT : 2,
+    V_ALIGN_IDX_TOP : 0,
+    V_ALIGN_IDX_CENTER : 1,
+    V_ALIGN_IDX_BOTTOM : 2,
+
+    CAL_TYPE:["percentage","abstract"],
+    CAL_TYPE_PERCENTAGE: 0,
+    CAL_TYPE_ABSTRACT: 1,
+
+    EVENT_TYPE: ["GRP_ON_CREATE", "FLOW_CONTENT_ON_CREATE"],
+    EVENT_IDX_GRP_ON_CREATE: 0,
+    EVENT_IDX_FLOW_CONTENT_ON_CREATE: 1,
+
+    PAGE_ORIENTATION_V_FIRST: 0,
+    PAGE_ORIENTATION_H_FIRST: 1,
+
+    ORIENTATION_PORTRAIT: "PORTRAIT",
+    ORIENTATION_LANDSCAPE: "LANDSCAPE",
+    ORIENTATION_PORTRAIT_CHN: "纵向",
+    ORIENTATION_LANDSCAPE_CHN: "横向",
+    SIZE_A3: [11.69, 16.54],
+    SIZE_A4: [8.27, 11.69],
+    SIZE_A5: [5.83, 8.27],
+    SIZE_B5: [6.93, 9.84],
+    SIZE_LETTER: [8.5, 11.0],
+    SIZE_LEGAL: [8.5, 14.0],
+    SIZE_16K: [7.75, 10.75],
+    SIZE_EXECUTIVE: [7.25, 10.5],
+
+    OUTPUT_OFFSET: [2,2,1,3],
+    OFFSET_IDX_LEFT: 0,
+    OFFSET_IDX_RIGHT: 1,
+    OFFSET_IDX_TOP: 2,
+    OFFSET_IDX_BOTTOM: 3,
+
+    PROP_PAGE_SEQ: "page_seq",
+    PROP_PAGE_MERGE_BORDER: "page_merge_border",
+    PROP_CELLS: "cells",
+
+    PAGING_OPTION_NORMAL: 'normal',
+    PAGING_OPTION_INFINITY: 'infinity',
+
+    OUTPUT_TYPE_NORMAL: 'normal',
+    OUTPUT_TYPE_SVG: 'svg',
+    OUTPUT_TYPE_PDF: 'pdf',
+    OUTPUT_TYPE_EXCEL: 'excel',
+
+    DISPLAY_VAL_TYPE_NORMAL: 0,
+    DISPLAY_VAL_TYPE_GROUP: 1,
+    DISPLAY_VAL_TYPE_AUTO_HEIGHT: 2,
+
+    TYPE_FOLLOW_MODE: 1,
+
+    PAGE_SELF_DEFINE: "自定义",
+    PAGE_SPECIAL_MERGE_POS: "page_merge_pos",
+
+    PAGES_SIZE_STR: ["A3", "A4", "A5", "B5", "LETTER", "LEGAL", "EXECUTIVE", "16K"],
+    PAGES_SIZE_IDX: [8, 9, 11, 13, 1, 5, 7, 93],
+    PAGES_SIZE: [[11.69, 16.54], [8.27, 11.69], [5.83, 8.27], [6.93, 9.84], [8.5, 11.0], [8.5, 14.0], [7.25, 10.5], [7.25, 10.5]],
+
+    HUNDRED_PERCENT : 100.0,
+
+    VERTICAL_ANGLE: "90",
+    ANTI_VERTICAL_ANGLE: "-90",
+    VERTICAL_ANGLE_INT: 90,
+    ANTI_VERTICAL_ANGLE_INT: -90,
+
+    LAST_DEF: ""
+};

+ 22 - 0
router.js

@@ -0,0 +1,22 @@
+/**
+ * Created by Tony on 2020/3/11.
+ */
+
+const express = require('express');
+const rptRouter = express.Router();
+const reportController = require("./modules/controllers/rpt_controller");
+
+module.exports =function (app) {
+
+    //now is the real:
+    rptRouter.get('/createExcelFiles', reportController.createExcelFilesEx);
+    rptRouter.get('/createExcelFilesInOneBook', reportController.createExcelFilesInOneBookEx);
+    rptRouter.get('/createPdfFiles', reportController.createPdfFilesEx);
+
+    rptRouter.get('/getFileByUUID/:uuid/:rptName/:suffix', reportController.getFileByUUID);
+
+    rptRouter.get('/restfulTestResponse', reportController.restfulTestResponse);
+    rptRouter.post('/restfulTestResponsePost', reportController.restfulTestResponsePost);
+
+    app.use("/report_api", rptRouter);
+};

+ 63 - 2
startService.js

@@ -4,5 +4,66 @@
 
 const express = require('express');
 
-let app = express();
-let _rootDir = __dirname;
+const app = express();
+const _rootDir = __dirname;
+
+app.locals.rootDir = _rootDir;
+app.use(express.static(_rootDir));
+
+// app.set('views', path.join(_rootDir, 'web'));
+// app.engine('.html', require('ejs').__express);
+// app.set('view engine', 'html');
+
+let bodyParser = require('body-parser');
+app.use(bodyParser.json());
+// app.use(bodyParser.urlencoded({limit: '100mb', extended: false}));
+// app.use(bodyParser.json({limit: '100mb'}));
+
+// 登录状态全局判断
+
+//加载路由文件
+// fileUtils.getGlobbedFiles('./modules/**/routes/*.js').forEach(function(modelPath) {
+//     if(modelPath.indexOf("import/routes")==-1) require(path.resolve(modelPath))(app);//排除掉导出导入服务
+// });
+
+app.all("*", function(request, response, next) {
+    response.header("Access-Control-Allow-Origin", "*"); // 设置跨域的域名,* 代表允许任意域名跨域
+    response.header("Access-Control-Allow-Headers", "X-Requested-With");
+    response.header(
+        "Access-Control-Allow-Methods",
+        "PUT,POST,GET,DELETE,OPTIONS"
+    );
+    response.header("X-Powered-By", " 3.2.1");
+    response.header("Content-Type", "application/json;charset=utf-8");
+    next();
+});
+
+app.use(express.static(_rootDir+"/web"));
+app.use(express.static(_rootDir+"/lib"));
+// let Router = require("./router");
+// app.use("/export",Router);
+require("./router")(app);
+
+
+//-----------------
+
+app.use(function(req, res, next) {
+    res.status(404).send('404 Error');
+});
+app.use(function(err, req, res, next) {
+    console.error(err.stack);
+    res.status(500).send('500 Error');
+});
+
+//设置外增的Date对象Format函数
+//备注: 经过测试nodejs 8.9.3版本不支持eval的方式修改prototype,为兼容考虑,把方法调整到stringUtil文件里
+require('./public/stringUtil').setupDateFormat();
+
+app.listen(3036, function(){
+    console.log('server started!');
+});
+
+//设置schedule job
+//1. 报表临时文件
+// rptCronJob.createJob(null, _rootDir + "/tmp");
+//2. 其他(待补充)...