luckysheet打印按钮功能实现,及重写getScreenshotNew打印截图支持插入图片及图表

目前使用lukysheet做报表设计器,在开发过程中发现luckysheet插件功能很全面,因此选用此组件,但免费东东,功能虽好但bug也多。

原工具栏中的打印按钮未实现功能,因此本人重新实现了功能。同时发现在原api:getScreenshot在生成选区截图时,不能将插入图片、统计图生成到截图中,因此对原函数进行扩展:getScreenshotNew。

如下具体介绍打印功能扩展:

思路:

(1)在自己项目嵌入lucksheet的页面,增加打印按钮,并设置不可见

(2)在lucksheet的源码中,将工具栏的打印按钮点击事件中,增加页面打印按钮的事件触发

(3)在自己项目中增加打印按钮事件功能实现(用于自动实现有数据区域选中,同时调用getScreenshotNew生成选取截图)

(4)将截图放到页面打印区域(因重写打印截图,当中用到html2canvas截图,html2canvas是异步生成函数,因此只能是在html2canvas执行完成后,通过函数委托方式触发打印)

(5)触发打印(控件使用的是vue-print-nb)

找到lucksheet源码:\Luckysheet\src\controllers\menuButton.js,修改函数:$("#luckysheet-icon-print").click

//print
$("#luckysheet-icon-print").click(function(){
    //获取页面的按钮控件,在点击菜单时触发页面按钮点击事件
    if($("#printPreviewBtn"))
    {
        $("#printPreviewBtn").click();
    }
    //将源项目代码全部注释,只用上面的代码
}

回到自己的项目中,在嵌入luckysheet的页面中,增加打印按钮用于进行打印功能实现:

html部分的代码:
<a-button id="printPreviewBtn" style="display: none;" @click="printExcel()">打印预览</a-button>
<a-button id="printExcelBtn" style="display: none;" plain
v-print="{ id: 'print_html', popTitle: 'test111' }">打印</a-button>
<div id="print_html" class="procedure" style="text-align: center;z-index: 0;"></div>
js部分的代码:
// 打印预览事件实现:
async printExcel() {
    this.Loading=true;
    this.printSheet();
},
/** 打印操作:自动选中打印区域,并生成打印截图,进行打印 */
printSheet() {
    document.querySelector('#print_html').style = "display:block";
    window.luckysheet.hideGridLines();
    //获取当前选中区域
    let currentSelected = luckysheet.getRange()
    //如果当前选中区只是一个单元格,则认为选取无效。
    if (currentSelected[0] != null && (currentSelected[0].row[1] - currentSelected[0].row[0] >= 1 || currentSelected[0].column[1] - currentSelected[0].column[0] >= 1)) {
        // 将打印区域生成base64图片(*将生成的base64编码复制粘贴到浏览器地址框,是可以预览图片样式的),生成后执行的后续打印操作,取用匿名委托函数做为参数传入
        luckysheet.getScreenshotNew((imgSrc) => {
            this.Loading=false;
            window.luckysheet.showGridLines();
            // * Lodop中的ADD_PRINT_IMAGE,也可以直接输出base64码图片,不用加img标签(如果加了img标签,会被当做超文本对待,受浏览器引擎解析的影响)
            let $img = `<img src=${imgSrc} style="max-width: 90%;" />`
            this.$nextTick(() => {
                document.querySelector('#print_html').innerHTML = $img;
            })
            document.getElementById('printExcelBtn').click();
            console.log(document.getElementById('printExcelBtn'));
            setTimeout(() => {
                document.querySelector('#print_html').style = "display:none";
            }, 100);
        });
    }
    else {
        // 获取打印区域的行列
        let RowColumn = this.getPrintSheetArea();
        // 因需要打印左边的边框,需重新设置第一列
        //RowColumn.column[0] = 0;
        // 进行选区操作
        luckysheet.setRangeShow(RowColumn);
        // 将打印区域生成base64图片(*将生成的base64编码复制粘贴到浏览器地址框,是可以预览图片样式的),生成后执行的后续打印操作,取用匿名委托函数做为参数传入
        luckysheet.getScreenshotNew((imgSrc) => {
            this.Loading=false;
            window.luckysheet.showGridLines();
            // * Lodop中的ADD_PRINT_IMAGE,也可以直接输出base64码图片,不用加img标签(如果加了img标签,会被当做超文本对待,受浏览器引擎解析的影响)
            let $img = `<img src=${imgSrc} style="max-width: 90%;" />`
            this.$nextTick(() => {
                document.querySelector('#print_html').innerHTML = $img;
            })
            document.getElementById('printExcelBtn').click();
            console.log(document.getElementById('printExcelBtn'));
            setTimeout(() => {
                document.querySelector('#print_html').style = "display:none";
            }, 100);
        });
    }

},
// 获取打印区域(即表格中有内容、非空白的区域)(用row,column数组表示)
getPrintSheetArea() {
    const sheetData = luckysheet.getSheetData();
    let objRowColumn = {
        row: [0, 0], //行
        column: [0, 0], //列
    };
    // * item是行、index是行索引、it是一行里的一格、itemIndex是这一格在这一行里的列索引
    sheetData.forEach((item, index) => {
        //行数
        item.forEach((it, itemIndex) => {
            if (it !== null) {
                if (objRowColumn.row[1] < index) {
                    objRowColumn.row[1] = index; //row第二位
                }
                if (objRowColumn.column[1] < itemIndex) {
                    objRowColumn.column[1] = itemIndex; //column第二位
                }
            }
        });
    });
    return objRowColumn;
},

再回到luckysheet项目源码,新增getScreenshotNew截图函数:src\global\api.js:

/**
 * 将选中区域进行截图,截图完成后调用指定委托函数,(修复支持插入的图片及图表截图):ALAN-JI
 * @param {Object} options 可选参数
 * @param {Object | String} options.range 选区范围,只能为单个选区;默认为当前选区
 * @param {function} action 委托执行函数,当所有绘图完成后,执行此委托函数(委托函数中可用于触发图片打印,或图片显示)
 */
export function getScreenshotNew(action, options = {}) {
    let {
        range = Store.luckysheet_select_save[Store.luckysheet_select_save.length - 1],
    } = { ...options }

    if (getObjType(range) == 'string') {
        if (!formula.iscelldata(range)) {
            return tooltip.info("The range parameter is invalid.", "");
        }

        let cellrange = formula.getcellrange(range);
        range = {
            "row": cellrange.row,
            "column": cellrange.column
        };
    }

    if (getObjType(range) != 'object' || range.row == null || range.column == null) {
        return tooltip.info("The range parameter is invalid.", "");
    }

    let str = range.row[0],
        edr = range.row[1],
        stc = range.column[0],
        edc = range.column[1];

    let has_PartMC = hasPartMC(Store.config, str, edr, stc, edc);

    if (has_PartMC) {
        return tooltip.info('Cannot perform this operation on partially merged cells', '');
    }

    let visibledatarow = Store.visibledatarow;
    let visibledatacolumn = Store.visibledatacolumn;

    let scrollHeight, rh_height;
    if (str - 1 < 0) {
        scrollHeight = 0;
        rh_height = visibledatarow[edr];
    }
    else {
        scrollHeight = visibledatarow[str - 1];
        rh_height = visibledatarow[edr] - visibledatarow[str - 1];
    }

    //根据当前分辨率计算出A4纸宽度,高度(高度不用限制,默认会自动分页)
    let a4Size=calculateA4PaperSize();
    let scrollWidth, ch_width=a4Size.width;
    if (stc - 1 < 0) {
        scrollWidth = 0;
        ch_width = visibledatacolumn[edc]<a4Size.width ? visibledatacolumn[edc]:a4Size.width;
    }
    else {
        scrollWidth = visibledatacolumn[stc - 1];
        ch_width = visibledatacolumn[edc] - visibledatacolumn[stc - 1]<a4Size.width ? visibledatacolumn[edc] - visibledatacolumn[stc - 1]:a4Size.width;
    }

    let newCanvas = $("<canvas>").attr({
        width: Math.ceil(ch_width * Store.devicePixelRatio),
        height: Math.ceil(rh_height * Store.devicePixelRatio)
    }).css({ width: ch_width, height: rh_height });

    luckysheetDrawMain(scrollWidth, scrollHeight, ch_width, rh_height, 1, 1, null, null, newCanvas);
    
    let ctx_newCanvas = newCanvas.get(0).getContext("2d");

    //补上 左边框和上边框
    ctx_newCanvas.beginPath();
    ctx_newCanvas.moveTo(
        0,
        0
    );
    ctx_newCanvas.lineTo(
        0,
        Store.devicePixelRatio * rh_height
    );
    ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    ctx_newCanvas.stroke();
    ctx_newCanvas.closePath();

    ctx_newCanvas.beginPath();
    ctx_newCanvas.moveTo(
        0,
        0
    );
    ctx_newCanvas.lineTo(
        Store.devicePixelRatio * ch_width,
        0
    );
    ctx_newCanvas.lineWidth = Store.devicePixelRatio * 2;
    ctx_newCanvas.strokeStyle = luckysheetdefaultstyle.strokeStyle;
    ctx_newCanvas.stroke();
    ctx_newCanvas.closePath();
    
    //获取插入图片的元素,并在canvas上进行绘制
    if ($('#luckysheet-image-showBoxs')) {
        var imgs = $('#luckysheet-image-showBoxs img');
        imgs.each(function (i) {
            var parent = $(this).parent().parent();
            var left = parent.css("left").replace('px', '');
            var top = parent.css("top").replace('px', '');
            var width = parent.css("width").replace('px', '');
            var height = parent.css("height").replace('px', '');
            var img = new Image()
            img.src = $(this).attr("src");
            ctx_newCanvas.drawImage(img, left, top, width, height);
        });
    }
    //获取统计图元素,并在canvas上进行绘制
    let targetDoms = document.querySelectorAll('.luckysheet-modal-dialog.luckysheet-modal-dialog-chart.luckysheet-data-visualization-chart');
    var chartCount = 0;
    if (targetDoms && targetDoms.length > 0)
    {
        repaintChartScreenshot(newCanvas,ctx_newCanvas,targetDoms,action,0,targetDoms.length);
    }
    
    if (!targetDoms || targetDoms.length == 0) {
        if (action) {
            action(newCanvas.get(0).toDataURL("image/png", 1));
        }
    }
}

/**
 * 递归获取所有统计图,并重绘chart到截屏图上:ALAN-JI
 * @param {object} newCanvas canvas画布元素对象
 * @param {object} ctx_newCanvas 当前画布用于绘制新图的对象
 * @param {NodeListOf} targetDoms 所有统计图Nodes集合
 * @param {function} action 委托函数,当所有绘图完成后,执行此委托函数(委托函数中可用于触发图片打印,或图片显示)
 * @param {int} index 当前统计图下标
 * @param {int} length 统计图总集合长度
 */
export function repaintChartScreenshot(newCanvas,ctx_newCanvas,targetDoms,action,index,length) {
    let targetDom = targetDoms[index];
    var left = targetDom.style.left.replace('px', '');
    var top = targetDom.style.top.replace('px', '');
    var width = targetDom.style.width.replace('px', '');
    var height = targetDom.style.height.replace('px', '');

    // 此处是实现滚动元素长截图的关键 start
    let copyDom = targetDom.cloneNode(true)
    copyDom.setAttribute('id', 'copyDom')  // 更改id 避免与targetDom id重复
    copyDom.style.width = targetDom.scrollWidth + 'px'
    copyDom.style.height = targetDom.scrollHeight + 'px'
    var chartCanvas = targetDom.getElementsByTagName("canvas")[0];
    var canvasToImg = new Image();
    canvasToImg.src = chartCanvas.toDataURL('image/png', 1);
    canvasToImg.style = chartCanvas.style;
    copyDom.innerHTML = copyDom.innerHTML.replace(chartCanvas.outerHTML, canvasToImg.outerHTML)
    copyDom.style.zIndex=-50
    document.querySelector('body').appendChild(copyDom);
    // 此处是实现滚动元素长截图的关键 end

    /* 
    *如不需要长截图,或者要截取的元素无滚动即完全显示。
    *下方要截取的元素改为targetDom,并把copyDom相关代码删除即可 
    */
    // html2canvas截屏,屏幕有滚动条时截图为空
    html2canvas(copyDom, {
        backgroundColor: "transparent",
        allowTaint: true,
        useCORS: true,
        scale: window.devicePixelRatio * 3,
        logging: false,
        imageTimeout: 15000,
        removeContainer: true,
    }).then(canvas => {
        var chartImg = new Image()
        chartImg.src = canvas.toDataURL('image/png', 1);
        document.querySelector('body').removeChild(copyDom)
        chartImg.onload = function () {
            index++;
            ctx_newCanvas.drawImage(chartImg, left, top, width, height);
            if (index >= length && action) {
                action(newCanvas.get(0).toDataURL("image/png", 1));
            }
            else
            {
                //递归调用重绘图片
                repaintChartScreenshot(newCanvas,ctx_newCanvas,targetDoms,action,index,length);
            }
        }
    });
}

以上完成后,就可以实现打印的全部功能了。具体最终效果如下:

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
实现一个图片按钮,你可以使用 Android 中的 ImageButton 控件。ImageButton 控件是 Button 控件的子类,它允许你在按钮中显示一个图像。 如果你想要自定义 ImageButton 控件,可以创建一个继承自 ImageButton 的子类,并重写其中的方法。下面是一个 Android 自定义 ImageButton 的示例代码: ``` public class CustomImageButton extends ImageButton { public CustomImageButton(Context context) { super(context); } public CustomImageButton(Context context, AttributeSet attrs) { super(context, attrs); } public CustomImageButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 在这里绘制你自己的图像 // 例如,你可以使用 Canvas 对象来绘制一个圆形图像 Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.BLUE); canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, paint); } } ``` 在这个示例中,我们创建了一个名为 `CustomImageButton` 的子类,并重写了其中的 `onDraw` 方法。在 `onDraw` 方法中,我们使用 Canvas 对象来绘制一个蓝色的圆形图像。你可以根据自己的需求来绘制其它的图像。 要在布局文件中使用这个自定义 ImageButton,你可以像使用普通的 ImageButton 一样,使用完全限定的类名来引用它: ``` <com.example.myapplication.CustomImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/my_image" /> ``` 在这个示例中,我们使用 `com.example.myapplication.CustomImageButton` 来引用我们自己定义的 ImageButton 控件。我们也指定了 `android:src` 属性来设置 ImageButton 中显示的图像。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值