赞
踩
由于业务需求需要做一个发票识别内容,然后就有了这个文章。首先这个思路是识别图片上的二维码信息,扫码二维码之后,我们就可以得到发票的基础信息:1、发票类型;2、发票代码;3、发票号码;4、发票效验码;5、发票开具时间;6、发票金额。这些信息就是发票二维码包含的所有的信息了。
其中使用的前端插件:qrcode-decoder,pdfjs-dist(如果你不考虑识别pdf的内容也可以忽略此内容)。
- <el-upload
- class="upload-demo mt5"
- style="height: 268px; width: 99%"
- drag
- :show-file-list="false"
- :auto-upload="false"
- action="#"
- method="POST"
- ref="fileUploadRef"
- accept=".pdf,.jpg,.png,.jpeg"
- :on-change="fileChange"
- >
- <el-icon class="el-icon--upload" style="font-size: 90px; line-height: 90px">
- <upload-filled />
- </el-icon>
- <div class="el-upload__text">拖拽或点击上传发票 <em>仅能自动识别支持带有二维码的发票</em></div>
- <div class="el-upload__text">支持格式:pdf、jpg、png等文件</div>
- </el-upload>
2.监听方法
- async function fileChange(rawFile) {
- let fileTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg', 'application/pdf'];
- if (!fileTypes.includes(rawFile.raw.type)) {
- console.log('存在不支持的格式');
- return false;
- }
- // ts写法 可换成this
- imgUrl.value = null;
- pdfUrl.value = null;
- // 可以用于在浏览器上预览本地图片或者视频,文件
- let fileUrl = getObjectURL(rawFile.raw);
- // 判断是否是pdf,如果是pdf就需要转成图片在扫码
- if (rawFile.raw.type === 'application/pdf') {
- pdfUrl.value = fileUrl;
- await pdfToImg(rawFile.raw, img => {
- dealScanQrCode(img);
- });
- } else {
- // 如果是图片就直接扫描二维码
- imgUrl.value = fileUrl;
- dealScanQrCode(imgSrc.value);
- }
- return true;
- }
3.封装扫描二维码
- // 扫描识别二维码的方法封装
- function dealScanQrCode(imgSrc) {
- // 具体的扫描方法
- scanQrCode(null, imgSrc, data => {
- let dealCode = [];
- // data 是返回的数据 判断data是否有异常
- if (data) {
- //解析后的数据是01,10,044002015551,94449434,548.13,20221117,7649489616555713831,7W7C,
- dealCode = data.split(',');
- // 如果分个后小于6说明不是发票信息
- if (dealCode.length < 6) {
- dealCode = [];
- }
- }
- // [0]占位符 [1]、发票类型{01增值税专用发票,04增值税普通发票,10增值税普通发票(电子),08增值税专用发票(电子)};
- // [2]、发票代码;[3]、发票号码;[4]开票金额;[5]、发票开具时间;[6]表示发票校验码,增值税专用发票是没有发票校验码的,没有则为空字符串;
- // 解析数据
- let row ={};
- // 发票类型
- row .invoiceType = dealCode[1] || null;
- // 发票代码
- row.invoiceCode = dealCode[2] || null;
- // 发票号码
- row.invoiceNo = dealCode[3] || null;
- let invoiceTotal = dealCode[4] || null;
- if (invoiceTotal) {
- invoiceTotal = parseFloat(invoiceTotal);
- }
- // 开票金额
- row.invoiceTotal = invoiceTotal;
- // 这里是价税合计的算法 首先你得有税率 一般都是13%
- let invoiceTaxRatio = row.invoiceTaxRatio || null;
- if (invoiceTaxRatio && invoiceTaxRatio > 0 && invoiceTotal) {
- // costInvoiceTotal价税合计
- let num = invoiceTaxRatio / 100;
- let invoiceTaxTotal = (invoiceTotal * num).toFixed(2);
- row.costInvoiceTotal = parseFloat(invoiceTotal) + parseFloat(invoiceTaxTotal);
- // invoiceTaxTotal税额
- row.invoiceTaxTotal = parseFloat(invoiceTaxTotal);
- }
- // 发票开具时间
- let invoiceTime = dealCode[5] || null;
- if (invoiceTime) {
- // 转换开票金额
- invoiceTime = invoiceTime.replace(/^(\d{4})(\d{2})(\d{2})$/, '$1-$2-$3');
- }
- row.invoiceTime = invoiceTime;
- });
- }
4.封装工具 utils.js
- // 扫码插件
- import QrCode from 'qrcode-decoder';
- // pdf转图片
- import * as pdfJs from 'pdfjs-dist';
- // pdfworker地址
- import * as pdfWorkerMin from 'pdfjs-dist/build/pdf.worker.min?url';
- // 这里要初始化一下路由防止异常,不然转换的时候会报错
- pdfJs.GlobalWorkerOptions.workerSrc = pdfWorkerMin.default;
- /**
- * 文件转成预览链接
- * @param file
- * @returns {null}
- */
- export const getObjectURL = file => {
- let url = null;
- if (window.createObjectURL !== undefined) {
- // basic
- url = window.createObjectURL(file);
- } else if (window.URL !== undefined) {
- // mozilla(firefox)
- url = window.URL.createObjectURL(file);
- } else if (window.webkitURL !== undefined) {
- // webkit or chrome
- url = window.webkitURL.createObjectURL(file);
- }
- return url;
- };
-
- /**
- * 识别二维码
- * @param file 文件
- * @param url 地址
- * @param callback 回调函数
- */
- export const scanQrCode = (file, url, callback) => {
- // 获取临时路径
- let tempUrl = url || '';
- if (file) {
- tempUrl = getObjectURL(file);
- }
- // 初始化
- const qr = new QrCode();
- // 解析二维码,返回promise
- qr.decodeFromImage(tempUrl).then(res => {
- if (callback) {
- callback(res.data);
- }
- });
- };
- /**
- * pdf转图片
- * @param file 文件信息
- * @param callBack 回调函数
- */
- export const pdfToImg = (file, callBack) => {
- // 文件转获取pdf地址
- let url = getObjectURL(file);
- //这里是重点,然后将流数据转换为url,CMapReaderFactory方法在进行处理
- pdfJs.getDocument(url).promise.then(pdf => {
- // pdf多个文件的情况下
- for (let num = 1; num <= pdf.numPages; num++) {
- // 分页查询
- pdf.getPage(num).then(async page => {
- // 获取pdf具体数据
- const viewport = page.getViewport({ scale: 1.0 });
- // 创建画布
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- // 处理画布高宽
- canvas.height = viewport.height;
- canvas.width = viewport.width;
- // 读取数据进行画布转换
- page.render({ canvasContext: context, viewport: viewport }).promise.then(() => {
- if (callBack) {
- // 出发回调函数
- callBack(canvas.toDataURL('image/jpeg'));
- // 释放资源
- canvas.remove();
- }
- });
- });
- }
- });
- };
注意!注意!注意 !
这里pdf转图片的时候是没有处理字符库的(会抛出异常信息,不影响操作),转换的后的发票图片是空白的没有具体的内容但是有二维码信息,如果你需要处理的话,请自行查询方法。
异常截图:
Warning: loadFont - translateFont failed: "UnknownErrorException: The CMap "baseUrl" parameter must be specified, ensure that the "cMapUrl" and "cMapPacked" API parameters are provided.".
5.预览
- <!-- 图片直接用element的图片插件-->
- <el-image v-if="imgUrl" :src="imgUrl" style="width: 100%; height: 268px" fit="contain">
- <template #placeholder>
- <div class="image-slot">加载中<span class="dot">...</span></div>
- </template>
- </el-image>
- <!-- pdf直接调用浏览器的预览-->
- <iframe v-if="pdfUrl" :src="pdfUrl + `#toolbar=0&zoom=50 `" width="100%" height="268" frameborder="0" />
- 图片直接用element的图片插件
- pdf直接调用浏览器的预览 这里携带了参数信息toolbar=0&zoom=50 ,toolbar=0代表不需要工具栏,zoom=50 是代表缩放了50%
End;
由于项目实际应用中,发现识别成功率过低,而且扫描件经常识别不到二维码,或者带有icon的二维码,识别率也惨不忍睹,后面引入了opencv-js-qrcode进行再次优化提升,提高了识别率。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。