赞
踩
一.先上效果图(全部代码见文章尾部)
二.整体思路
上面部分是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.$set
-
- this.$forceUpdate(); //有时候页面无法刷新,用这个方法
五.整体代码
- <template>
- <div
- class="invoice-form"
- v-loading="pageLoading"
- element-loading-text="发票识别中..."
- >
- <el-carousel
- indicator-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">
- <i
- v-if="opeType != 'edit'"
- class="el-icon-delete upload-delete-cl"
- @click.stop="deleteImg(mainIndex)"
- ></i>
- <el-upload
- action="/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">
- <div
- class="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">
- <div
- class="upload-img-box middle-center"
- v-if="mainItem.invoiceImage"
- >
- <img
- class="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-item
- class="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-item
- class="item-cl"
- :label-width="labelWidth"
- label="发票号码"
- prop="invoiceNo"
- >
- <el-input
- clearable
- :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-item
- class="item-cl"
- :label-width="labelWidth"
- label="发票代码"
- prop="invoiceCode"
- >
- <el-input
- clearable
- :disabled="opeType == 'edit'"
- placeholder="请输入发票代码"
- class="input-cl"
- size="small"
- v-model.trim="mainItem.invoiceCode"
- />
- </el-form-item>
- <el-form-item
- class="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-item
- class="item-cl"
- :label-width="labelWidth"
- label="合计金额"
- prop="totalAmt"
- >
- <el-input-number
- size="small"
- class="input-cl"
- v-model="mainItem.totalAmt"
- controls-position="right"
- :precision="2"
- :min="0"
- ></el-input-number>
- </el-form-item>
- <el-form-item
- class="item-cl"
- :label-width="labelWidth"
- label="票面金额"
- prop="invoiceAmt"
- >
- <el-input-number
- size="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-item
- class="item-cl"
- :label-width="labelWidth"
- label="税额"
- prop="taxAmt"
- >
- <el-input-number
- size="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">
- <div
- v-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">
- <div
- v-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">
- <img
- style="width: 100%"
- v-if="mainItem.invoiceImage"
- :src="$baseUrl + mainItem.invoiceImage"
- />
- <el-empty
- v-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) {
- //第一张发票覆盖,后面的push
- this.$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:ss
- if (!str) return '';
- str = str.substr(0, 2) +
- ":" +
- str.substr(2, 2) +
- ":" +
- str.substr(4, 2);
- return str;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。