日常生活工作学习中,大家对电子表格必定不陌生。从工作数据汇总分析到出门收据各种电子发票,这些都是由电子表格制作出来的。

不过大家对电子表格的印象可能停留在这里:

标准行列数据统计的表格样式。

但其实,表格也可以是这样的:

工作中遇到需要实现的表格情况往往比大家想象的要更加复杂,最近我们在做客户支持的工作过程中遇到了一个客户,他需要借助电子表格表格实现合同中的电子签名。

电子签名通俗来说就是通过技术手段实现在电子文档上加载电子形式的签名,其作用类似于纸质合同上的手写签名或加盖的公章。在企业工作流审批、请柬、单据保全等场景应用广泛。

在经济活跃跨区域化现象越来越多的今天,作为电子表格的一个重要使用场景,电子合同可以实现异地签约,签署的时间第点更加自由;面对大批量的合同签署也可以轻松解决;同时传统纸质合同的管理更加方便,避免了纸质合同因保存管理问题而出现损坏。

而今天,客户在实际项目中需要实现的内容长这样:

看到这里,有些小伙伴可能会说这有什么难的,虽然这个东西长相酷似word,

但不就是电子表格去掉边框线吗?

如果只是简单的表格框内容,下段代码就可以简单的实现表格的绘制。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>02Canvas案例-绘制表格</title>
</head>
<body>
<div id="container">
    <canvas id="cavsElem">

    </canvas>
</div>
<script>
    (function(){
        var canvas=document.querySelector('#cavsElem');
        var ctx=canvas.getContext('2d');
        canvas.width=600;
        canvas.height=600;
        canvas.style.border='1px solid green';
        var rectH=10;
        var rectW=20;
        ctx.lineWidth=.5;
        //绘制表格
        // 第一步: 绘制横线
        for(var i=0;i<canvas.width;i++){
            ctx.moveTo(rectW*i,0);
            //如果不设置moveTo,当前画笔没有位置
            ctx.lineTo(rectW*i,canvas.height);
        }
        //第二步:绘制竖线:如果绘制的格子的宽高相等,可以将for循环放到一个里面;
        for(var i=0;i<canvas.height;i++){
            ctx.moveTo(0,rectH*i);
            ctx.lineTo(canvas.width,rectH*i);
        }
        ctx.stroke();
    }())
</script>
</body>
</html>

但是放大仔细看看,就会发现情况并不如我们所想的这么简单。

在这个合同中,我们除了要隐藏边框线,还要考虑边缘留白、图片跨越、页面滚动后截图不全等问题。 而借助电子表格在数据处理和分析方面天生具备的优势,可以很容易的实现电子签名功能。

我们今天就一起来尝试通过基于Canvas的电子表格来实现电子签名并导出PDF的项目开发需求。

实现思路

环境准备

  1. 环境准备:安装SpreadJS 前端表格插件,并通过插件绘制canvas画布。

  2. 初始化Spread工作簿,并导入合同模板

  3. 创建Canvas画布并引用esign.js画法实现手写签名区域

  4. 通过自定义超链接跳转命令,签名区域呼出

  5. 将签名区域转化为图片设置为背景图片

  6. 使用SpreadJS提供的导出PDF接口将签署的文件导出

电子签名的实现

初始化Spread工作簿

1、 引入以下文件

<link rel="stylesheet" type="text/css" href="node_modules/@grapecity/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<script src="node_modules/@grapecity/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script>
<script src="new2.ssjson" type="text/javascript"></script>

2、 创建用于承载SpreadJS的DOM

<div id="ss" class="sample-spreadsheets" style="height: 900px;">

3、 用JS获取DOM对象并进行初始化

var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"));

4、 导入合同模板

spread.fromJSON(str);

到这里,我们Spread工作簿已经初始化完成了。当然,你也可以添加对应的CSS调整表单的大小。

关于模板的制作,你可以在在线表格编辑器中根据需求进行绘制,并导出为ssjson文件并通过fromJSON导入到我们的表单中。

接下来,用Canvas画布来实现手写签名区域。

手写签名区域

1、首先,我们先创建签名区域的DOM元素,并定义一个Canvas画布,默认情况下不显示。

<div class="containter" id="box" style="display: none;">
       <div class="canvasDiv">
             <div id="editing_area">
                  <canvas id="canvasEdit"></canvas>
             </div>
       </div>
       <div class="btnDiv">
             <a id="sign_clear" class="clearBtn">清空</a>
             <a id="sign_clear2" class="clearBtn">签署</a>
       </div>
</div>

2、引用esign.js和jQuery。Esign.js是一种用鼠标在canvas上绘制的画法。

<script type="text/javascript" src="js/esign.js"></script>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>

3、初始化

$(document).esign("canvasEdit", "sign_show", "sign_clear", "sign_ok");
$(document).on('click', '#sign_clear2', takeScreenshot);

Canvas画布中利用自定义单元格,理论上也是能开发出能够直接签名的单元格。

用户可以直接在单元格进行签名,有兴趣的小伙伴可以尝试用自定义单元格实现。

自定义超链接命令

1、 创建超链接

sheet.setValue(32, 10, "审核人签名:")
sheet.setHyperlink(32, 10, { command: "popup" });

2、 为超链接设置命令,点击弹出画布

spread.commandManager().register("popup",{
    canUndo: true,
    execute: function (context, options, isUndo) {
    var Commands = GC.Spread.Sheets.Commands;
    // 在此加cmd
    options.cmd = "popup";
    if (isUndo) {
            Commands.undoTransaction(context, options);
                    return true;
            } else {
                    Commands.startTransaction(context, options);

                    document.getElementById("box").style.display = "block";

                    Commands.endTransaction(context, options);
                        return true;
                    }
            }
    });

指定DOM转为图片并设置为单元格背景

1、利用canvas的接口,将画布转为base64,调用接口设置背景

  function convertCanvasToImage(canvas) {
                    return canvas.toDataURL("image/png");
   };

  function takeScreenshot() {        
      var canvas = document.getElementById("canvasEdit");
      var imgUrl = convertCanvasToImage(canvas); //截取图片路径,该路径为服务器参数
      var sheet = spread.getSheet(0);  
      sheet.getCell(32,13).backgroundImage(imgUrl);
      sheet.getCell(35,13).backgroundImage(imgUrl);
      sheet.getCell(38,13).backgroundImage(imgUrl);
}

2、关闭签名画布

function tishi(){ 
      document.getElementById("box").style.display = "none";
} 
setTimeout(tishi,100)

将电子签名导出PDF

上面已经实现了电子签名内容,但是我们都知道合同需要有打印输出功能,接下来我们继续介绍如何使用pdf打印输出电子签名。

1、引用PDF拓展文件以及filesaver

<script src="node_modules/@grapecity/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js" type="text/javascript"></script>
<script src="node_modules/file-saver/dist/FileSaver.min.js" type="text/javascript"></script>

2、调用接口导出PDF

spread.savePDF(function (blob) {
    var fileName = 'download';
    saveAs(blob, fileName + '.pdf');
}, function (error) {
    console.log(error);
}, {
    title: 'Test Title',
});

注意:导出中文字符需要注册对应的字体。

总结

以上,我们实现了基于Canvas电子表格实现电子签名并使用PDF导出打印的完整功能,由于Canvas完全取代了页面的dom结构,因此打印时不需要遍历要打印的dom节点的子节点,也不必将每一页所能打印的dom节点高度累加,这样做可以不用再计算dom节点的高度,大幅节省了系统性能,同时实现了较细的页面颗粒度,不会造成大块空白的情况,完全模拟出了word生成pdf的那种效果。同时,也解决了我们在文章开头中提到缘留白、图片跨越、页面滚动后截图不全三个问题。

我们接下来还会为大家带来更多在工作项目中遇到的有趣内容。