持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
在工作中遇到需要将后端传过来的对象用 canvas 画出发票
一、 封装工具函数
- 第一个参数是发票数据对象
- 第二个是 canvas 的dom元素,我们这里使用 document.getElementById('这里填那个canvas的id') 来获取
- canvas.toDataURL 将画布转换成base64的数据格式
export drawMain = (data, el) => {
var canvas = el;
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
canvas.height = canvas.height;
ctx.strokeRect(60, 17, 108, 108);//二维码
ctx.font = "14px serif";
ctx.fillText(data.kpjh, 125, 145);//机器编号
ctx.fillText(data.fphm, 889 + 27, 56);//发票号码
ctx.fillText(data.fpdm, 889 + 27, 85);//发票代码
ctx.fillText(data.kprq, 889 + 27, 114);//开票日期
if (data.jym) {
ctx.fillText(data.jym.substring(0, 5) + ' ' + data.jym.substring(5, 10) + ' ' + data.jym.substring(10, 15) + ' ' + data.jym.substring(15, 20) + ' ' + data.jym.substring(20, 25), 808 + 27 + 81, 143);//效验码
}
ctx.fillText(data.gfmc, 38 + 43 + 5 + 115, 155 + 29 - 7);//名称
ctx.fillText(data.gfsh, 38 + 43 + 5 + 115, 155 + 29 + 29 - 7);//纳税人识别号
ctx.fillText(data.gfdzdh, 38 + 43 + 5 + 115, 155 + 29 + 29 + 29 - 7);//地 址、电 话
ctx.fillText(data.gfyhzh, 38 + 43 + 5 + 115, 155 + 29 + 29 + 29 + 29 - 7);//开户行及账号
ctx.fillText(data.xfmc, 38 + 43 + 5 + 115, 155 + 15 + 4 + 117 + 278 + 6);//名称
ctx.fillText(data.xfsh, 38 + 43 + 5 + 115, 155 + 29 + 15 + 2 + 117 + 278 - 2 + 4);//纳税人识别号
ctx.fillText(data.xfdzdh, 38 + 43 + 5 + 115, 155 + 29 + 29 + 15 + 2 + 117 + 278 - 4 + 2);//地 址、电 话
ctx.fillText(data.xfyhzh, 38 + 43 + 5 + 115, 155 + 29 + 29 + 29 + 15 + 2 + 117 + 278 - 6);//开户行及账号
if (data.bz) {
ctx.fillText(data.bz.substring(0, 52), 38 + 43 + 570 + 28 + 5, 155 + 15 + 4 + 117 + 278 + 6);//备注第一行
ctx.fillText(data.bz.substring(52, 104), 38 + 43 + 570 + 28 + 5, 155 + 29 + 15 + 2 + 117 + 278 - 2 + 4);//备注第二行
ctx.fillText(data.bz.substring(104, 104 + 52), 38 + 43 + 570 + 28 + 5, 155 + 29 + 29 + 15 + 2 + 117 + 278 - 4 + 2);//备注第三行
ctx.fillText(data.bz.substring(104 + 52, 208), 38 + 43 + 570 + 28 + 5, 155 + 29 + 29 + 29 + 15 + 2 + 117 + 278 - 6);//备注第四行
}
ctx.fillText('¥' + (parseFloat(data.hjje+'')+parseFloat(data.hjse+'')), 38 + 277 + 138 + 64 + 106 + 106 + 160 + 15 + 30, 155 + 117 + 278 - 15);//(小写)
ctx.fillText(toChies(parseFloat(data.hjje+'')+parseFloat(data.hjse+'')), 38 + 136 + 136 + 15, 155 + 117 + 278 - 15);//价税合计(大写)
ctx.fillText(data.skr, 38 + 43 + 5 + 35, 155 + 503 + 20);//收款人
ctx.fillText(data.fhr, 38 + 80 + 277 + 35, 155 + 503 + 20);//复核
ctx.fillText(data.kpr, 38 + 43 + 570 + 35, 155 + 503 + 20);//开票人
if (data.qdbz == '1') {
ctx.fillText('*详见销货清单*', 38 + 5, 155 + 117 + 40 + 2 + 4);//明细
ctx.textAlign = "right";
ctx.fillText('¥' + data.hjje, 38 + 277 + 138 + 64 + 106 + 106 + 160 - 5, 155 + 117 + 40 + 2 + 4);//合计金额
ctx.fillText('¥' + data.hjse, 38 + 1068 - 5, 155 + 117 + 40 + 2 + 4);//合计税额
} else {
for (let i = 0; i < data.xxfpMxList.length; i++) {
let temp = data.xxfpMxList[i];
ctx.textAlign = "start";
ctx.fillText(temp.spmc, 38 + 5, 155 + 117 + 40 + (20 * i) + 2 + 2 * i + i + 4);
ctx.textAlign = "right";
ctx.fillText(temp.ggxh, 38 + 277 + 138 - 5, 155 + 117 + 40 + 2 + (20 * i) + 2 * i + i)//规格型号
ctx.fillText(temp.jldw, 38 + 277 + 138 + 64 - 5, 155 + 117 + 40 + 2 + (20 * i) + 2 * i + i)//单位
ctx.fillText(temp.sl, 38 + 277 + 138 + 64 + 106 - 5, 155 + 117 + 40 + 2 + (20 * i) + 2 * i + i)//数量
ctx.fillText('¥' +temp.dj, 38 + 277 + 138 + 64 + 106 + 106 - 5, 155 + 117 + 40 + 2 + (20 * i) + 2 * i + i)//单价
ctx.fillText('¥' +temp.je, 38 + 277 + 138 + 64 + 106 + 106 + 160 + -5, 155 + 117 + 40 + 2 + (20 * i) + 2 * i + i)//金额
ctx.fillText(temp.slv*100+'%', 38 + 277 + 138 + 64 + 106 + 106 + 160 + 60 - 5, 155 + 117 + 40 + 2 + (20 * i) + 2 * i + i)//税率
ctx.fillText('¥' + temp.se, 38 + 1068 - 5, 155 + 117 + 40 + 2 + (20 * i) + 2 * i + i)//税额
}
}
ctx.textAlign = "right";
ctx.fillText('¥' + data.hjje, 38 + 277 + 138 + 64 + 106 + 106 + 160 - 5, 155 + 117 + 278 - 50);//合计金额
ctx.fillText('¥' + data.hjse, 38 + 1068 - 5, 155 + 117 + 278 - 50);//合计税额
ctx.fillStyle = "#A66A4C";
ctx.strokeStyle = "#A66A4C";
let title='';
if (
data.fpzl == 'p'
) {
title = '增值税电子普通发票'
}
if ( data.fpzl == 's') {
title = '增值税专用发票'
}
if ( data.fpzl == 'c') {
title = '增值税普通发票'
}
ctx.font = "48px serif";
ctx.textAlign = "center";
ctx.fillText(title, 571, 84);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('机器编号:', 44, 145);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('发票代码:', 808 + 27, 56);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('发票号码:', 808 + 27, 85);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('开票日期:', 808 + 27, 114);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('效 验 码:', 808 + 27, 143);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('购', 38 + 21, 155 + 29 + 5);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('买', 38 + 21, 155 + 29 + 29 + 5);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('方', 38 + 21, 155 + 29 + 29 + 29 + 5);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('销', 38 + 21, 155 + 29 + 5 + 117 + 278);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('售', 38 + 21, 155 + 29 + 29 + 5 + 117 + 278);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('方', 38 + 21, 155 + 29 + 29 + 29 + 5 + 117 + 278);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('名 称:', 38 + 43 + 5, 155 + 29 - 7);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('纳税人识别号:', 38 + 43 + 5, 155 + 29 + 29 - 7);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('地 址、电 话:', 38 + 43 + 5, 155 + 29 + 29 + 29 - 7);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('开户行及账号:', 38 + 43 + 5, 155 + 29 + 29 + 29 + 29 - 7);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('名 称:', 38 + 43 + 5, 155 + 15 + 4 + 117 + 278 + 6);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('纳税人识别号:', 38 + 43 + 5, 155 + 29 + 15 + 2 + 117 + 278 - 2 + 4);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('地 址、电 话:', 38 + 43 + 5, 155 + 29 + 29 + 15 + 2 + 117 + 278 - 4 + 2);
ctx.font = "14px serif";
ctx.textAlign = "start";
ctx.fillText('开户行及账号:', 38 + 43 + 5, 155 + 29 + 29 + 29 + 15 + 2 + 117 + 278 - 6);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('货物或应税劳务、服务名称', 38 + 136, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('合 计', 38 + 136, 155 + 117 + 278 - 50);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('(小写)', 38 + 277 + 138 + 64 + 106 + 106 + 160 + 15, 155 + 117 + 278 - 15);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('价税合计(大写)', 38 + 136, 155 + 117 + 278 - 15);
ctx.fillText('收款人:', 38 + 43 + 5, 155 + 503 + 20);
ctx.fillText('复核:', 38 + 80 + 277, 155 + 503 + 20);
ctx.fillText('开票人:', 38 + 43 + 570, 155 + 503 + 20);
ctx.fillText('销售方:(章)', 38 + 277 + 138 + 64 + 106 + 106 + 160 + 35, 155 + 503 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('规格型号', 38 + 277 + 69, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('单位', 38 + 277 + 138 + 32, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('数量', 38 + 277 + 138 + 64 + 53, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('单价', 38 + 277 + 138 + 64 + 106 + 53, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('金 额', 38 + 277 + 138 + 64 + 106 + 106 + 80, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('税率', 38 + 277 + 138 + 64 + 106 + 106 + 160 + 30, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('税 额', 38 + 277 + 138 + 64 + 106 + 106 + 160 + 60 + 80, 155 + 117 + 20);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('密', 38 + 43 + 570 + 14, 155 + 29 + 5);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('码', 38 + 43 + 570 + 14, 155 + 29 + 29 + 5);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('区', 38 + 43 + 570 + 14, 155 + 29 + 29 + 29 + 5);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('备', 38 + 43 + 570 + 14, 155 + 278 + 117 + 40);
ctx.font = "14px serif";
ctx.textAlign = "center";
ctx.fillText('注', 38 + 43 + 570 + 14, 155 + 278 + 117 + 27 + 55);
ctx.beginPath();
ctx.moveTo(373, 97);
ctx.lineTo(769, 97);
ctx.closePath();
ctx.stroke();//标题线条
ctx.beginPath();
ctx.moveTo(373, 101);
ctx.lineTo(769, 101);
ctx.closePath();
ctx.stroke();//标题线条
ctx.strokeRect(38, 155, 1068, 503);//发票体外框
ctx.beginPath();
ctx.moveTo(38 + 43, 155);
ctx.lineTo(38 + 43, 155 + 117);//购买方右边框
ctx.moveTo(38 + 43 + 570, 155);
ctx.lineTo(38 + 43 + 570, 155 + 117);//密码区左边框
ctx.moveTo(38 + 43 + 570 + 28, 155);
ctx.lineTo(38 + 43 + 570 + 28, 155 + 117);//密码区右边框
ctx.moveTo(38, 155 + 117);
ctx.lineTo(38 + 1068, 155 + 117); //明细上边框
ctx.moveTo(38, 155 + 117 + 236);
ctx.lineTo(38 + 1068, 155 + 117 + 236);//明细下边框
ctx.moveTo(38, 155 + 117 + 278);
ctx.lineTo(38 + 1068, 155 + 117 + 236 + 42);//价税合计下边框
ctx.moveTo(38 + 277, 155 + 117);
ctx.lineTo(38 + 277, 155 + 117 + 278);//商品名右边框
ctx.moveTo(38 + 277 + 138, 155 + 117);
ctx.lineTo(38 + 277 + 138, 155 + 117 + 236);//规格型号右边框
ctx.moveTo(38 + 277 + 138 + 64, 155 + 117);
ctx.lineTo(38 + 277 + 138 + 64, 155 + 117 + 236); //单位右边框
ctx.moveTo(38 + 277 + 138 + 64 + 106, 155 + 117);
ctx.lineTo(38 + 277 + 138 + 64 + 106, 155 + 117 + 236);//数量右边框
ctx.moveTo(38 + 277 + 138 + 64 + 106 + 106, 155 + 117);
ctx.lineTo(38 + 277 + 138 + 64 + 106 + 106, 155 + 117 + 236);//单价右边框
ctx.moveTo(38 + 277 + 138 + 64 + 106 + 106 + 160, 155 + 117);
ctx.lineTo(38 + 277 + 138 + 64 + 106 + 106 + 160, 155 + 117 + 236);//金额右边框
ctx.moveTo(38 + 277 + 138 + 64 + 106 + 106 + 160 + 60, 155 + 117);
ctx.lineTo(38 + 277 + 138 + 64 + 106 + 106 + 160 + 60, 155 + 117 + 236);//税率右边框
ctx.moveTo(38 + 43, 155 + 117 + 278);
ctx.lineTo(38 + 43, 155 + 503);//销售方右边框
ctx.moveTo(38 + 43 + 570, 155 + 117 + 278);
ctx.lineTo(38 + 43 + 570, 155 + 503);//备注左边框
ctx.moveTo(38 + 43 + 570 + 28, 155 + 278 + 117);
ctx.lineTo(38 + 43 + 570 + 28, 155 + 503);//备注右边框
ctx.stroke();
// 画布 --> base64
let url = canvas.toDataURL('image/jpg')
/**这里填入不同的代码片段,使函数达到不同的效果
*/
}
}
// 转大写
let toChies =function(amount) { //形参
// 汉字的数字
const cnNums = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
// 基本单位
const cnIntRadice = ["", "拾", "佰", "仟"];
// 对应整数部分扩展单位
const cnIntUnits = ["", "万", "亿", "兆"];
// 对应小数部分单位
const cnDecUnits = ["角", "分"];
// 整数金额时后面跟的字符
const cnInteger = "整";
// 整型完以后的单位
const cnIntLast = "元";
// 最大处理的数字
const maxNum = 9999999999999999.99;
// 金额整数部分
let integerNum;
// 金额小数部分
let decimalNum;
// 输出的中文金额字符串
let chineseStr = "";
// 分离金额后用的数组,预定义
let parts;
if (amount === "") {
return "";
}
amount = parseFloat(amount);
if (amount >= maxNum) {
// 超出最大处理数字
return "";
}
if (amount === 0) {
chineseStr = cnNums[0] + cnIntLast + cnInteger;
return chineseStr;
}
// 转换为字符串
amount = amount.toString();
if (amount.indexOf(".") === -1) {
integerNum = amount;
decimalNum = "";
} else {
parts = amount.split(".");
integerNum = parts[0];
decimalNum = parts[1].substr(0, 4);
}
// 获取整型部分转换
if (parseInt(integerNum, 10) > 0) {
let zeroCount = 0;
const IntLen = integerNum.length;
for (let i = 0; i < IntLen; i++) {
const n = integerNum.substr(i, 1);
const p = IntLen - i - 1;
const q = p / 4;
const m = p % 4;
if (n === "0") {
zeroCount++;
} else {
if (zeroCount > 0) {
chineseStr += cnNums[0];
}
// 归零
zeroCount = 0;
//alert(cnNums[parseInt(n)])
chineseStr += cnNums[parseInt(n)] + cnIntRadice[m];
}
if (m === 0 && zeroCount < 4) {
chineseStr += cnIntUnits[q];
}
}
chineseStr += cnIntLast;
}
// 小数部分
if (decimalNum !== "") {
const decLen = decimalNum.length;
for (let i = 0; i < decLen; i++) {
const n = decimalNum.substr(i, 1);
if (n !== "0") {
chineseStr += cnNums[Number(n)] + cnDecUnits[i];
}
}
}
if (chineseStr === "") {
chineseStr += cnNums[0] + cnIntLast + cnInteger;
} else if (decimalNum === "") {
chineseStr += cnInteger;
}
return chineseStr;
}
1. 代码片段一
这个片段实现的是点击按钮,将canvas画好的发票下载到本地
let a = document.createElement('a')
let event = new MouseEvent('click')
a.download = data.fphm + '-' + data.fpdm
a.href = url
a.dispatchEvent(event)
2. 代码片段二
这个片段实现的是点击按钮,将canvas画好的发票转成 file 可以发送
// base64 --> file
let arr = url.split(','),
bstr = atob(arr[1]),
mime = arr[0].match(/:(.*?);/)[1], //图片后缀
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let blob = new Blob([u8arr], {type:mime })
blob.lastModifiedDate = new Date()
二、使用
这里的画布要隐藏起来 style="display: none"
<button @click="downLoad"></button>
<div style="display: none">
<canvas id="tutorial" width="1142" height="738"></canvas>
</div>
downLoad() {
let objInvioce = {
"fpdm": "4433263500",
"gfsh": "91330621769630618N",
"hjse": 734.50,
"xxfpMxList": [
{
"ggxh": "物料开票规格",
"se": 146.90,
"jldw": "KG",
"dj": 100.00000000,
"sl": 10.00000000,
"je": 1000.00,
"slv": 0.13
},
{
"ggxh": "物料开票规格",
"se": 146.90,
"jldw": "KG",
"dj": 100.00000000,
"sl": 10.00000000,
"je": 1000.00,
"slv": 0.13
} "slv": 0.13
],
"gfmc": "成都旭光电子股份有限公司",
"gfyhzh": "中国工商银行股份有限公司成都马超西路支行 440294300902216611",
"kpr": "五",
"fpzl": "s",
"hjje": 5000.00,
"xfyhzh": "1 11",
"kpjh": "00",
"fhr": "四",
"kprq": "2022-06-27 15:41:59",
"gfdzdh": "成都市新都区新都镇新工大道318号 028-83967159 028-83967159",
"bz": null,
"xfdzdh": "1 13905719774",
"id": 180,
"xfsh": "test",
"skr": "三",
"xfmc": "1",
"fphm": "09395297",
"qdbz": "0"
},
}
drawMain(objInvioce,document.getElementById('tutorial'))