一.先上效果图(全部代码见文章尾部)

二.整体思路

上面部分是v-for遍历生成走马灯的item标签(el-carousel-item),el-form放在每个标签里面去动态生成。

底部是自己写的一个滚动块,滚动块index和上面走马灯item的index下标实时保持对应。

应用流程/业务逻辑如下:(ocr模块支持图片和多张发票的pdf文件扫描上传)

新增:

       点击底部滑块+号选择图片/pdf上传  -> 读取后端返回的数组对象,遍历并添加到el-carousel-item里面,移动滑块index到最新(上面的图片如果是照片就回显,如果是pdf文件,就显示一个默认logo,点击图片/pdf都打开新页面显示出来)  -> 编辑好信息后点击确定 -> 验证是否有重复,有重复阻止提交,并提示出重复的发票。

编辑:

        编辑我这里业务相对简单,编辑的时候回显表单数据,隐藏底部滑块(意味着+号的新增也去掉了),发票号码,代码不可编辑,只可以对部分基础数据进行编辑。

三.入参

oriForm:表单数据,接收为数组,el-form根据这个值生成多组form表单

opeType:是新增还是编辑  参数为add/edit

props: {oriForm: {type: Array,default: [],},opeType: {type: String,default: "",},},

四.关键难点/坑点代码

<el-form:ref="'dataForm' + mainIndex"        这里ref动态赋予
>
<el-upload:ref="'elUpload' + mainIndex"    上传文件的ref动态赋予
>
this.$set(this.model[active], "statementNo", this.settlementNo);    //form表单给值得时候用this.$setthis.$forceUpdate(); //有时候页面无法刷新,用这个方法

五.整体代码

<template><divclass="invoice-form"v-loading="pageLoading"element-loading-text="发票识别中..."><el-carouselindicator-position="none":autoplay="false"ref="elCarousel":loop="false"height="490px"@change="changeCarousel"><el-carousel-item:name="mainIndex.toString()"v-for="(mainItem, mainIndex) in model":key="mainIndex"><el-form:model="mainItem":ref="'dataForm' + mainIndex":rules="formRule"><div class="invoice-img middle-center"><iv-if="opeType != 'edit'"class="el-icon-delete upload-delete-cl"@click.stop="deleteImg(mainIndex)"></i><el-uploadaction="/personal/ocr/invoice":ref="'elUpload' + mainIndex":disabled="opeType == 'edit'"drag:limit="1":show-file-list="false":before-upload="beforeUpload":on-exceed="onExceed":on-error="onError":on-success="(row, info, c) => {return onSuccess(row, info, c, mainIndex);}"class="el-upload-block"><div class="top-img-box"><divclass="full-box"@click.stop="openPdf(mainItem.invoiceImage)"v-if="mainItem.contentType == 'application/pdf'"><div class="block-one middle-center"><img src="../imgs/pdf4.jpg" class="pdf-img-cl" /></div><div class="pdf-name-cl">{{ mainItem.fileName }}</div></div><div v-else class="full-box middle-center"><divclass="upload-img-box middle-center"v-if="mainItem.invoiceImage"><imgclass="top-img-cl":src="$baseUrl + mainItem.invoiceImage"/></div><div v-else><i class="el-icon-upload"></i><div class="el-upload__text">将发票文件拖到此处自动识别,或<em>点击上传</em></div></div></div></div></el-upload></div><el-row type="flex"><el-form-itemclass="item-cl":label-width="labelWidth"label="对账单编号"prop="statementNo"><select-remote:ref="'statementSelectRemote' + mainIndex"class="input-cl":disabled="opeType == 'edit'"keyName="settlementNo"labelName="settlementNo"size="small"valueName="settlementNo"remoteUrl="/personal/credent/reconciliation/list"@change="(row) => changeStatement(row, mainIndex)"placeholder="支持对账单编号模糊查询"queryField="settlementNo":pagination="true":resultLine="['data', 'list']"></select-remote></el-form-item><el-form-itemclass="item-cl":label-width="labelWidth"label="发票号码"prop="invoiceNo"><el-inputclearable:disabled="opeType == 'edit'"placeholder="请输入发票号码"class="input-cl"size="small"v-model.trim="mainItem.invoiceNo"/></el-form-item></el-row><el-row type="flex"><el-form-itemclass="item-cl":label-width="labelWidth"label="发票代码"prop="invoiceCode"><el-inputclearable:disabled="opeType == 'edit'"placeholder="请输入发票代码"class="input-cl"size="small"v-model.trim="mainItem.invoiceCode"/></el-form-item><el-form-itemclass="item-cl":label-width="labelWidth"label="开票日期"prop="billingShow"><el-date-picker:unlink-panels="true"size="small"v-model.trim="mainItem.billingShow"type="datetime"value-format="yyyy-MM-dd HH:mm:ss"placeholder="请选择开票日期"class="input-cl":clearable="false"></el-date-picker></el-form-item></el-row><el-row type="flex"><el-form-itemclass="item-cl":label-width="labelWidth"label="合计金额"prop="totalAmt"><el-input-numbersize="small"class="input-cl"v-model="mainItem.totalAmt"controls-position="right":precision="2":min="0"></el-input-number></el-form-item><el-form-itemclass="item-cl":label-width="labelWidth"label="票面金额"prop="invoiceAmt"><el-input-numbersize="small"class="input-cl"v-model="mainItem.invoiceAmt"controls-position="right":precision="2":min="0"></el-input-number></el-form-item></el-row><el-row type="flex"><el-form-itemclass="item-cl":label-width="labelWidth"label="税额"prop="taxAmt"><el-input-numbersize="small"class="input-cl"v-model="mainItem.taxAmt"controls-position="right":min="0":precision="2"></el-input-number></el-form-item></el-row></el-form></el-carousel-item></el-carousel><!-- 底部滚动模块 --><div class="img-scoll middle-center" v-if="opeType != 'edit'"><div class="left-point middle-center"><i class="el-icon-arrow-left arr-cl" @click="moveLeft"></i></div><div class="middle-block"><divv-for="(mainItem, mainIndex) in model":class="{'img-block-one': true,'middle-center': true,'active-class': carouselIndex == mainIndex,}":key="mainIndex"@click="clickImg(mainIndex)"><div class="for-center"><divv-if="mainItem.contentType == 'application/pdf'"class="bottom-img-box middle-center"><img style="width: 70%" src="../imgs/pdf4.jpg" /></div><div v-else class="bottom-img-box middle-center"><imgstyle="width: 100%"v-if="mainItem.invoiceImage":src="$baseUrl + mainItem.invoiceImage"/><el-emptyv-else:image-size="50"description="描述文字"></el-empty></div></div></div><div class="img-block-one middle-center" @click="addNewInvoice"><div class="for-center middle-center"><i class="el-icon-plus add-invoice"></i></div></div></div><div class="right-point middle-center" @click="moveRight"><i class="el-icon-arrow-right arr-cl"> </i></div></div></div>
</template><script>
import { getInvoiceFormRules } from "../configFiles/invoiceFormRules.js";
import selectRemote from "@/components/select/selectRemote.vue";
import { changeTime, changeDate } from "@/utils/public.js";
import {addInvoice,fileDetail,updateInvoice,getSelectRegistList,
} from "@/api/personalApi.js";
export default {components: { selectRemote },props: {oriForm: {type: Array,default: [],},opeType: {type: String,default: "",},},watch: {oriForm: {handler(val) {this.model = JSON.parse(JSON.stringify(val));console.log(this.model);},immediate: true,},},created() {console.log("传入的数据为:", this.model);this.formRule = getInvoiceFormRules(); //规则加载this.$nextTick(() => {this.oriData(); //针对1个需要懒加载的select框进行处理});if (this.opeType == "edit") {this.loadFileInfo(); //加载文件信息this.formatTime();}},data() {return {labelWidth: "100px",model: [],pageLoading: false,carouselIndex: 0, //幻灯片到哪一张了settlementNo: "", //一组发票信息只有一个对账单编号,记录编号};},methods: {oriData() {//查询对账单数据let _params = {settlementNo: "",pageNum: 1,pageSize: 20,};if (this.opeType == "edit") {_params.settlementNo = this.model[0].statementNo; //对账单编号始终保持一致,任意取一个this.settlementNo = this.model[0].statementNo;}getSelectRegistList(_params).then((res) => {this.model.forEach((item, mainIndex) => {let _aimRef = `statementSelectRemote${mainIndex}`;if (this.opeType == "edit") {//编辑的情况this.$refs[_aimRef][0].loadFirstData(res, _params.settlementNo);} else {this.$refs[_aimRef][0].loadFirstData(res);}});});},changeCarousel(active, ori) {this.carouselIndex = active;//重新赋值一次最新的对账单编号this.$nextTick(() => {let _aimRef = `statementSelectRemote${active}`;this.$refs[_aimRef][0].initData(this.settlementNo);console.log("this.settlementN", this.settlementNo);this.$set(this.model[active], "statementNo", this.settlementNo);this.$forceUpdate();});},formatTime() {this.model.forEach((item, index) => {this.$set(this.model[index],"billingShow",changeDate(item.billingDate) + " " + changeTime(item.billingTime));});},loadFileInfo() {this.setFileInfo(this.model[0].invoiceImage);},clickImg(active) {this.$refs.elCarousel.setActiveItem(active);},moveLeft() {if (this.carouselIndex > 0) {this.clickImg(this.carouselIndex - 1);}},addNewInvoice() {let _judge = this.judgeForm();if (_judge._canAdd) {this.model.push({billingShow: "",});this.$nextTick(() => {this.$refs.elCarousel.setActiveItem(this.model.length - 1);});} else {this.$message({message: _judge._errorMsg,type: "warning",});}},openPdf(fileNo) {window.open(this.$baseUrl + fileNo, "_blank");},confirm() {let _judge = this.confirmJudgeForm();if (_judge._canAdd) {console.log("即将提交的数为:", this.model);//对时间做一下处理this.dealTime();if (this.opeType == "edit") {updateInvoice(this.model[0]).then((res) => {if (res.code == 200) {this.goSuccess("更新成功!");} else {this.goError("更新失败");}});} else {addInvoice(this.model).then((res) => {if (res.code == 200) {this.goSuccess("新增成功!");} else {this.goError("新增失败");}});}} else {this.$message({message: _judge._errorMsg,type: "warning",});}},dealTime() {this.model.forEach((item) => {item.billingDate = item.billingShow.split(" ")[0].replaceAll("-", "");item.billingTime = item.billingShow.split(" ")[1].replaceAll(":", "");// delete item.billingShow;});},goSuccess(msg) {this.$emit("btnConfirm");this.$message({message: msg,type: "success",});},goError(msg) {this.$message({message: msg,type: "error",});},confirmJudgeForm() {let fiJudge = this.judgeForm();if (!fiJudge._canAdd) {return fiJudge;}//继续对重复数据的校验let fKey,sKey,countArr = [],_canAdd = true,_errorMsg = "";this.model.forEach((item, index) => {countArr[index] = 0;});this.model.forEach((fItem, fIndex) => {fKey = fItem.invoiceNo + "-" + fItem.invoiceCode;this.model.forEach((sItem, sIndex) => {sKey = sItem.invoiceNo + "-" + sItem.invoiceCode;if (fKey == sKey) {countArr[fIndex]++;}});});let _model = this.model,reArr = [],_msg = "";countArr.forEach((item, index) => {if (item > 1) {_canAdd = false;_msg ="发票号码:" +_model[index].invoiceNo +"," +"发票代码:" +_model[index].invoiceCode +"数据存在重复;" +" ";reArr.push(_msg);}});reArr = new Set(reArr);reArr = Array.from(reArr);_errorMsg = reArr.join("");return {_canAdd,_errorMsg,};},judgeForm() {let _canAdd = true,_errorMsg = "存在未填写数据";this.model.forEach((item, index) => {let _aim = `dataForm${index}`;this.$refs[_aim][0].validate((valid) => {if (!valid) {_canAdd = false;return false;}});//同时检查一下照片的invoiceImage是否存在if (!item.invoiceImage) {_canAdd = false;_errorMsg = "存在发票信息未上传";}});return {_canAdd,_errorMsg,};},moveRight() {if (this.carouselIndex < this.model.length - 1) {this.clickImg(this.carouselIndex + 1);}},beforeUpload(file) {console.log("file文件为:", file);if (file.type == "image/jpeg" ||file.type == "image/png" ||file.type == "application/pdf") {this.pageLoading = true;return true;} else {this.$message({message: "仅支持jpg,png,pdf格式文件",type: "error",});return false;}},changeStatement(row, mainIndex) {this.settlementNo = row.value;this.model.forEach((item, index) => {this.$set(this.model[index], "statementNo", row.value);});},deleteImg(mainIndex) {if (this.model.length == 1) {this.$message.warning("至少保留一张发票");return;}this.$confirm(`确认删除当前发票?`, "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {this.model.splice(mainIndex, 1);this.$nextTick(() => {this.$refs.elCarousel.setActiveItem(mainIndex - 1);});this.$forceUpdate();});},onExceed() {this.$message.error("请先删除当前文件再进行上传");},onError() {this.pageLoading = false;},onSuccess(row, info, c, mainIndex) {console.log("发票返回信息为:", info);let _result = info.response;this.pageLoading = false;if (_result.code != 200) {this.$message({message: _result.msg,type: "error",});let _aimRef = `elUpload${mainIndex}`;this.$refs[_aimRef][0].clearFiles();return;}_result.data.forEach((item, reIndex) => {this.changeOriForm(this.model.length - 1, item, reIndex);});this.$forceUpdate();//再新增一个操作,将光标移动到最后一个位置this.$nextTick(() => {this.$refs.elCarousel.setActiveItem(this.model.length - 1);});this.$message.success("发票识别成功");},changeOriForm(_index, item, reIndex) {let _taxAmt = item.totalTax ? item.totalTax : "";let _billingShow = item.billingDate? this.changeTimeFormat(item.billingDate, item): "";let _invoiceAmt = item.amountTax ? item.amountTax : "";let _invoiceImage = item.fileNo ? item.fileNo : "";let _invoiceCode = item.invoiceCode ? item.invoiceCode : "";let _invoiceNo = item.invoiceNumber ? item.invoiceNumber : "";let _totalAmt = item.totalAmount ? item.totalAmount : "";let _billingTime = ""; //开票时间是没有的if (reIndex == 0) {//第一张发票覆盖,后面的pushthis.$set(this.model[_index], "taxAmt", _taxAmt); //税额this.$set(this.model[_index], "billingShow", _billingShow); //开票日期this.$set(this.model[_index], "invoiceAmt", _invoiceAmt); //票面金额this.$set(this.model[_index], "invoiceImage", _invoiceImage); //回显文件编号this.$set(this.model[_index], "invoiceCode", _invoiceCode); //发票代码this.$set(this.model[_index], "invoiceNo", _invoiceNo); //发票号码this.$set(this.model[_index], "totalAmt", _totalAmt); //合计金额console.log(this.model);} else {let _obj = {taxAmt: _taxAmt,billingShow: _billingShow,invoiceAmt: _invoiceAmt,invoiceImage: _invoiceImage,invoiceCode: _invoiceCode,invoiceNo: _invoiceNo,totalAmt: _totalAmt,};this.model.push(_obj);}this.setFileInfo(_invoiceImage); //同时将文件的类型合名称设置进去},setFileInfo(_invoiceNo) {let _index = this.model.length - 1;fileDetail({ fileNo: _invoiceNo }).then((res) => {this.$set(this.model[_index], "fileName", res.data.fileName);this.$set(this.model[_index], "contentType", res.data.contentType);});},changeTimeFormat(time, mainItem) {if (mainItem.billingTime) {return time + " " + item.billingTime;} else {return time + " " + "00:00:00";}},},
};
</script>
<style lang="scss" scoped>
::-webkit-scrollbar {width: 8px;height: 8px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.3);border-radius: 10px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {border-radius: 10px;background: rgba(0, 0, 0, 0.1);-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5);
}
.invoice-form {.full-box {width: 100%;height: 100%;}.invoice-img {width: 100%;.top-img-box {width: 100%;height: 100%;.block-one {width: 100%;margin-top: 20px;.pdf-img-cl {width: 160px;height: 160px;}}.pdf-name-cl {width: 100%;}}.upload-img-box {width: 100%;height: 100%;overflow: auto;.top-img-cl {width: 100%;}}.upload-delete-cl {position: absolute;right: 12%;cursor: pointer;top: 0px;font-size: 18px;}.el-upload-block {margin-bottom: 15px;width: 70%;height: 230px;/deep/.el-upload {width: 100%;.el-upload-dragger {width: 100%;display: flex;align-items: center;justify-content: center;height: 220px;}.el-upload-dragger .el-icon-upload {margin: auto;line-height: 80px;}}}}.img-scoll {width: 100%;height: 98px;display: flex;margin-top: 20px;.left-point {width: 25px;height: 100%;cursor: pointer;&:hover {background: #efefef;}}.middle-block {width: calc(100% - 72px);padding: 0px 6px;white-space: nowrap;overflow: auto;display: flex;/deep/.el-empty {padding: 0px;.el-empty__description {display: none;}}.img-block-one {width: 80px;display: inline-block;height: 80px;border: 1px solid lightgray;margin-right: 10px;cursor: pointer;.add-invoice {font-size: 25px;}.for-center {width: 100%;height: 100%;.bottom-img-box {width: 100%;height: 100%;}}}.active-class {border: 2px solid #3388fb;width: 79px;height: 79px;}}.right-point {width: 25px;height: 100%;cursor: pointer;&:hover {background: #efefef;}}.arr-cl {font-size: 30px;font-weight: bold;color: lightgray;cursor: pointer;}}.item-cl {width: 50%;.input-cl {width: 90%;/deep/.el-input {width: 100%;}}}
}
</style>

select-remote组件是我封装的远程搜索下拉组件,见前面的文章

invoiceFormRules.js文件是规则文件,类似如下:

export function getInvoiceFormRules() {return {statementNo: [{required: true,message: "请选择对账单编号",trigger: "change",}],}
}

changTime,changeDate方法比较简单

export function changeDate(str) { //将yyyyMMdd转换为yyyy-MM-dd if (!str) return '';str = str.substr(0, 4) +"-" +str.substr(4, 2) +"-" +str.substr(6, 2);return str;
}export function changeTime(str) { //将hhmmss转换为hh:mm:ssif (!str) return '';str = str.substr(0, 2) +":" +str.substr(2, 2) +":" +str.substr(4, 2);return str;
}

发票自动识别功能前端开发(多表单动态生成)相关推荐

  1. JavaWeb前端开发注册表单验证

    注册表单验证 最近在尝试学习开发一个网站,现将登录页面的表单验证总结如下 表单校验分析 1.用户名:单词字符,8到20位 2.密码:单词字符,8到20位 3.email:邮箱格式 4.姓名:汉字非空 ...

  2. 前端开发-4-HTML-tableform表单控制 标签

    1.table标签 <!DOCTYPE html> <html lang="cn"> <head><meta charset=" ...

  3. html表单 asp验证,ASP中JavaScript处理复杂表单的生成与验证

    ASP中JavaScript处理复杂表单的生成与验证 更新时间:2007年03月25日 00:00:00   作者: 这里所谓的复杂表单,是指表单中包含多种不同的输入类型,比如下拉列表框.单行文本.多 ...

  4. 获取form表单_【第1535期】前端 Form 的表单的一个通用解决方案

    前言 今日早读文章由阿里@布达投稿分享. @布达,Alibaba Fusion项目组的.花名潕量.主要专注在设计系统.组件.可视化搭建这个领域 正文从这开始-- Fusion Next - Form ...

  5. 开发指南专题九:JEECG微云快速开发平台-表单校验组件ValidForm

    开发指南专题九:JEECG微云快速开发平台表单校验组件ValidForm 10.表单校验组件ValidForm 10.1使用入门 1.引入css 请查看下载文件中的style.css,把里面Valid ...

  6. 学习前端——表格、表单

    学习前端--表格.表单 表格在日常生活中使用的非常的多,比好excel就是 专门用来创建表格的工具, 表格就是用来表示一些格式化的数据的,比如:课程表.银行对账单 在网页中也可以来创建出不同的表格. ...

  7. 开发指南专题十:JEECG微云快速开发平台--表单校验组件ValidForm

    开发指南专题十:JEECG微云快速开发平台--表单校验组件ValidForm 10.4Validform对象[方法支持链式调用] 如示例 var demo=$(".formsub" ...

  8. 前端:简述表单提交前如何进行数据验证

    前端:简述表单提交前如何进行数据验证 通常在提交表单数据时,我们会对数据进行验证,例如某些字段是必填字段,不能为空,这时应该如何做呢?有如下三种方法: 一.在button的submit事件进行判断 & ...

  9. 【前端】Ajax-form表单与模板引擎

    目录 一.form表单的基本使用 1.1什么是表单 1.2表单的组成部分 1.3form标签属性 1.4表单的同步提交及缺点 1.4.1什么是表单的同步提交 1.4.2表单同步提交的缺点 1.4.3如 ...

最新文章

  1. mfc 儿童算数作业生成器_9岁男孩一写作业就哭,爸妈毅然带孩子跳绳,打骂真的没用...
  2. 变量在原型链中的查找顺序
  3. 与善淘网一起做慈善商店
  4. Interview:算法岗位面试—11.05下午上海某银行信息(总行,四大行之一)技术岗笔试记录
  5. python循环捕捉异常_python异常捕捉以及处理
  6. TYVJ P1051 选课 Label:多叉转二叉树形dp(虐心♥)
  7. 如果编程语言是女孩子
  8. centos6.5安装配置zabbix3.0.3
  9. Windows核心思想-宽字符与窄字符(Unicode和ASCII)
  10. php 操作系统,PHP 处理文件和操作系统
  11. hdu2553N皇后问题(打表)
  12. docker-compose idea CreateProcess error=2, 系统找不到指定的文件
  13. HTML 标题居中 小小积累
  14. 利用 Python 进行量化投资分析 - 利率及风险资产的超额收益
  15. 最小生生树算法-prim/kruskal
  16. 【渝粤教育】广东开放大学 跨文化商务沟通 形成性考核 (42)
  17. qgg包(续)-小数据教程-数据分析
  18. 利用rfcomm实现树莓派与手机通信_树莓派可以这样玩
  19. Salesforce-Apex基础
  20. shell du命令

热门文章

  1. 老猿学5G扫盲贴:3GPP中的5G计费架构
  2. JavaSwing_4.8: JTable(表格)
  3. 天降大任与斯人也,成功是有原因的
  4. 值得收藏极速PDF阅读器和编辑器快捷键大全
  5. Attention Mechanism[Transformer、Transformer-XL、XLNet]
  6. HJKHGJKHGKJ
  7. gzip和gunzip 解压参数详解
  8. ARChon 分析之四:ARC的下载与编译
  9. 关于【finder不能完成该操作 因为未能读取或写入文件名中的某些数据(错误代码-36)】快速解决办法----本人备注
  10. Windows启动进阶-孙宇彤-专题视频课程