| | |
| | | <el-date-picker |
| | | style="width: 100%" |
| | | v-model="form.applyTime" |
| | | type="date" |
| | | type="date" format="yyyy-MM-dd" |
| | | value-format="yyyy-MM-dd HH:mm:ss" |
| | | placeholder="申请日期" |
| | | :disabled="true" |
| | |
| | | <el-form-item |
| | | label="捐献者" |
| | | prop="infoid" |
| | | v-show="form.costtype == 4" |
| | | v-show="form.costtype == 3 || form.costtype == 4" |
| | | > |
| | | <el-select |
| | | v-model="form.infoid" |
| | |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <div style="margin-left: 10px;"> |
| | | <dict-tag |
| | | :options="dict.type.sys_DonationStatus" |
| | | :value="form.invoicecount" |
| | | /> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row style="margin-top: 10px"> |
| | |
| | | <el-form-item label="出差人" prop="travelers"> |
| | | <el-select v-model="form.travelers" clearable filterable allow-create ref="getReportname" |
| | | default-first-option placeholder="请选择" @change="getAccountinfo()"> |
| | | <el-option v-for="item in travelers" :key="item.reportNo" :label="item.reportName" |
| | | <el-option v-for="item in travelers" :label="item.reportName" |
| | | :value="item.reportName"> |
| | | </el-option> |
| | | </el-select> |
| | |
| | | allow-create |
| | | default-first-option |
| | | placeholder="请选择" |
| | | @focus1="getPersons(scope.row)" |
| | | > |
| | | <el-option |
| | | v-for="item in personsSel" |
| | | :key="item.reportNo" |
| | | :label="item.reportName" |
| | | :key="item.id" |
| | | :label="item.reportNameDesc" |
| | | :value="item.reportName" |
| | | > |
| | | </el-option> |
| | |
| | | size="small" |
| | | style="width: 100%" |
| | | v-model="scope.row.starttime" |
| | | type="date" |
| | | type="date" format="yyyy-MM-dd" |
| | | value-format="yyyy-MM-dd HH:mm:ss" |
| | | placeholder="开始日期" |
| | | @blur="getdays(scope.row)" |
| | |
| | | size="small" |
| | | style="width: 100%" |
| | | v-model="scope.row.endtime" |
| | | type="date" |
| | | type="date" format="yyyy-MM-dd" |
| | | value-format="yyyy-MM-dd HH:mm:ss" |
| | | placeholder="结束日期" |
| | | @blur="getdays(scope.row)" |
| | |
| | | > |
| | | <el-option |
| | | v-for="item in personsSel" |
| | | :key="item.reportNo" |
| | | :label="item.reportName" |
| | | :value="item.reportName" |
| | | :key="item.id" |
| | | :label="item.reportNameDesc" |
| | | :value="item.id" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | |
| | | <!-- |
| | | <el-table-column prop="paiddate" slot="" label="打款日期" fixed align="center" width="150"> |
| | | <template slot-scope="scope"> |
| | | <el-date-picker clearable size="small" style="width: 100%" v-model="scope.row.paiddate" type="date" |
| | | <el-date-picker clearable size="small" style="width: 100%" v-model="scope.row.paiddate" type="date" format="yyyy-MM-dd" |
| | | value-format="yyyy-MM-dd HH:mm:ss" placeholder="打款日期" :disabled="true"> |
| | | </el-date-picker> |
| | | </template> |
| | |
| | | <el-col :span="12"> |
| | | <el-form-item label="预审材料" prop="infoid"> |
| | | <el-radio-group v-model="form.checkstatus" align="left"> |
| | | <el-radio label="1">待申请材料</el-radio> |
| | | <el-radio label="2">审核通过等待邮寄纸质材料</el-radio> |
| | | <el-radio label="3">已收到报销材料</el-radio> |
| | | <el-radio label="1">待申请</el-radio> |
| | | <el-radio label="2">审核通过等待纸质材料</el-radio> |
| | | <el-radio label="3">已收到纸质材料</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <!-- <el-image |
| | | style="width: 95%; height: 90%" |
| | | :src="pdfimg" |
| | | :preview-src-list="pdfimgsrcList" --> |
| | | > |
| | | :preview-src-list="pdfimgsrcList" |
| | | > --> |
| | | <el-image |
| | | ref="imagePreview" |
| | | style="width: 95%; height: 90%" |
| | |
| | | exportReimbursement, |
| | | getdownload, |
| | | checkfund, |
| | | fundSubmit |
| | | fundSubmit, |
| | | listbypowerone |
| | | } from "@/api/project/reimbursement"; |
| | | import { |
| | | addReimbursementdetail, |
| | |
| | | import { listReportname, listUser } from "@/api/project/organization"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import debounce from "lodash/debounce"; |
| | | import { moneySum, moneyAdd, moneyRound } from "@/utils/money"; |
| | | import { checkFileName, checkDetails } from "@/utils/fileNameCheck"; |
| | | export default { |
| | | components: { |
| | | Treeselect, |
| | |
| | | "sys_recordstatus", |
| | | "sys_travelexpensebelong", |
| | | "sys_traffictype", |
| | | "sys_area_name" |
| | | "sys_area_name", |
| | | "sys_DonationStatus" |
| | | ], |
| | | data() { |
| | | return { |
| | |
| | | baselisttages: [ |
| | | { |
| | | id: 1, |
| | | name: "待申请材料" |
| | | name: "待申请" |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: "审核通过等待邮寄纸质材料" |
| | | name: "预审通过等待纸质材料" |
| | | }, |
| | | { |
| | | id: 3, |
| | | name: "已收到报销材料" |
| | | name: "已收到纸质材料" |
| | | } |
| | | ], |
| | | baselist: [], |
| | |
| | | flowconclusion: null, |
| | | fundid: null |
| | | }, |
| | | //连续审批:待审批列表 |
| | | pendingList: [], |
| | | jurisdiction: false, |
| | | |
| | | //保存按钮控制 |
| | |
| | | if ( |
| | | (JSON.stringify(this.form) == sessionStorage.getItem("apiform") && |
| | | JSON.stringify(this.rbDetails) == |
| | | sessionStorage.getItem("apifunddetail")) || |
| | | sessionStorage.getItem("apifunddetail")) || |
| | | !sessionStorage.getItem("apifunddetail") |
| | | ) { |
| | | } else { |
| | |
| | | handleImageLoad() { |
| | | console.log("图片加载成功"); |
| | | }, |
| | | handleUploadError() {}, |
| | | handleUploadError() { }, |
| | | // 获取请求头 |
| | | Getnetworkheader() { |
| | | console.log(window.location.href); |
| | |
| | | }, |
| | | |
| | | //文件上传成功回调 |
| | | /** |
| | | * 2026-10-15:上传时校验附件文件名(业务要求:不合格立即提示且不加入列表)。 |
| | | * 规则与后端 AttachmentNameValidator 同口径:必须含中文 / 去掉扩展名后主干 ≤20 字 / |
| | | * 不含空格及 / \ : * ? " < > | # % 等特殊字符。 |
| | | */ |
| | | checkUploadFileName(file) { |
| | | const raw = file && file.raw ? file.raw.name : file && file.name; |
| | | const r = checkFileName(raw); |
| | | if (!r.ok) { |
| | | this.$modal.msgError(r.msg); |
| | | return false; |
| | | } |
| | | return true; |
| | | }, |
| | | |
| | | /** |
| | | * 2026-10-15:保存前校验整单明细附件文件名(含同一行内重名)。 |
| | | * 口径(业务确认):只校验"本次变更"的附件——老单据里已存在的不合规附件名不能把保存卡死 |
| | | * (否则老单据改一个金额都存不进去)。后端按 id 读旧记录放行历史附件;前端拿不到"哪些是历史" |
| | | * 的权威信息,因此**仅新增单据**做保存前拦截,编辑老单据时新增的不合规附件由后端二次校验拦下。 |
| | | */ |
| | | checkDetailFileNames() { |
| | | if (this.form && this.form.id) { |
| | | return true; |
| | | } |
| | | const c = checkDetails(this.rbDetails); |
| | | if (!c.ok) { |
| | | this.$modal.msgError(c.msg); |
| | | return false; |
| | | } |
| | | return true; |
| | | }, |
| | | |
| | | uploadSccess(response, file, fileList) { |
| | | if (!this.checkUploadFileName(file)) { |
| | | return; |
| | | } |
| | | this.rbDetails; |
| | | const config = { |
| | | headers: { Authorization: "Bearer " + this.ICDtoken } |
| | |
| | | name: file.name, |
| | | url: URL.createObjectURL(blob) |
| | | }); |
| | | // 2026-10-15:删除这里的第二次 push。原来一次上传会向 fileListto / pdfimgsrcList |
| | | // 各写两条,且第二条的 name 取的是 file.response.fileName(存储路径);而 remove() |
| | | // 会把整个 fileListto 回写到 rbDetails[atpresent].annexfilesList,导致明细附件里 |
| | | // 混入路径形态的 name,被附件文件名校验误拦(前端没有"路径形态跳过"豁免), |
| | | // 同时票据列表会多出一行。 |
| | | console.log(URL.createObjectURL(blob), "ur转换"); |
| | | }) |
| | | .catch(error => { |
| | | console.error("Error loading image", error); |
| | |
| | | const index = this.getIndexInArray(this.fileListto, row); |
| | | this.fileListto.splice(index, 1); |
| | | console.log(this.rbDetails[this.atpresent].annexfilesList); |
| | | indexvalue = this.rbDetails[this.atpresent].annexfilesList.find( |
| | | (item, index) => { |
| | | return item.name == row.name; |
| | | } |
| | | ); |
| | | console.log(indexvalue, "删除索引"); |
| | | this.rbDetails[this.atpresent].annexfilesList.splice(indexvalue, 1); |
| | | // indexvalue = this.rbDetails[this.atpresent].annexfilesList.find( |
| | | // (item, index) => { |
| | | // return item.name == row.name; |
| | | // } |
| | | // ); |
| | | // console.log(indexvalue, "删除索引"); |
| | | // this.rbDetails[this.atpresent].annexfilesList.splice(indexvalue, 1); |
| | | const targetIndex = this.rbDetails[ |
| | | this.atpresent |
| | | ].annexfilesList.findIndex(item => { |
| | | return item.name === row.name; // 建议使用严格相等 === |
| | | }); |
| | | console.log(targetIndex, "删除索引"); // 现在打印的是索引号,例如 0, 1, 2... |
| | | |
| | | if (targetIndex > -1) { |
| | | // 确保找到了元素 |
| | | this.rbDetails[this.atpresent].annexfilesList.splice(targetIndex, 1); |
| | | console.log("删除成功"); |
| | | } else { |
| | | console.log("未找到对应项目,无法删除"); |
| | | } |
| | | } else { |
| | | const indexlist = this.getIndexInArray( |
| | | this.invoicepdfimgsrcList, |
| | |
| | | const index = this.getIndexInArray(this.invoicefileListto, row); |
| | | this.invoicefileListto.splice(index, 1); |
| | | console.log(this.rbDetails[this.atpresent].invoicefilesList); |
| | | indexvalue = this.rbDetails[this.atpresent].invoicefilesList.find( |
| | | (item, index) => { |
| | | return item.name == row.name; |
| | | } |
| | | ); |
| | | console.log(indexvalue, "删除索引"); |
| | | this.rbDetails[this.atpresent].invoicefilesList.splice(indexvalue, 1); |
| | | const targetIndex = this.rbDetails[ |
| | | this.atpresent |
| | | ].invoicefilesList.findIndex(item => { |
| | | return item.name === row.name; // 建议使用严格相等 === |
| | | }); |
| | | console.log(targetIndex, "删除索引"); // 现在打印的是索引号,例如 0, 1, 2... |
| | | |
| | | if (targetIndex > -1) { |
| | | // 确保找到了元素 |
| | | this.rbDetails[this.atpresent].invoicefilesList.splice( |
| | | targetIndex, |
| | | 1 |
| | | ); |
| | | console.log("删除成功"); |
| | | } else { |
| | | console.log("未找到对应项目,无法删除"); |
| | | } |
| | | } |
| | | }, |
| | | // 点击上移 |
| | |
| | | } else if (row.persontype == "管理人员") { |
| | | this.personsSel = this.personsManager; |
| | | } |
| | | if (this.personsSel && this.personsSel.length) { |
| | | this.personsList.forEach(item => { |
| | | item.reportNameDesc = `${item.reportName}(${item.unitname})`; |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | //获取设置人员账号信息 |
| | |
| | | } |
| | | |
| | | for (let m = 0; m < dataArr.length; m++) { |
| | | if (dataArr[m].reportName == row.personname) { |
| | | if (dataArr[m].id == row.personname) { |
| | | row.personname = dataArr[m].reportName; |
| | | row.bankname = dataArr[m].depositbank; |
| | | row.bankcardno = dataArr[m].bankcardno; |
| | | } |
| | |
| | | row.days = ( |
| | | Math.floor( |
| | | Math.abs(Date.parse(row.starttime) - Date.parse(row.endtime)) / |
| | | (1000 * 3600 * 24) |
| | | (1000 * 3600 * 24) |
| | | ) + 1 |
| | | ).toString(); |
| | | } else { |
| | |
| | | if (tempStr == "杭州") { |
| | | row.otherexpense = ""; |
| | | } else if ( |
| | | tempStr == "杭州-桐庐" || |
| | | tempStr == "杭州-建德" || |
| | | tempStr == "杭州-淳安" |
| | | tempStr.includes("桐庐") || |
| | | tempStr.includes("建德") || |
| | | tempStr.includes("淳安") |
| | | ) { |
| | | row.otherexpense = (parseInt(row.days) * 60).toString(); |
| | | } else { |
| | |
| | | if (item.id == this.form.infoid) { |
| | | this.form.donorno = item.donorno; |
| | | this.form.donorname = item.name; |
| | | |
| | | // 新增:将捐献者的 recordstate 赋值到 invoicecount |
| | | if (item.recordstate !== undefined && item.recordstate !== null) { |
| | | this.form.invoicecount = item.recordstate.toString(); |
| | | } else { |
| | | this.form.invoicecount = ""; // 如果 recordstate 为空,清空字段 |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 添加清除方法 |
| | | clearDonatorInfo() { |
| | | this.form.donorno = ""; |
| | | this.form.donorname = ""; |
| | | this.form.invoicecount = ""; // 清空 invoicecount |
| | | }, |
| | | onpick(e) { |
| | | // console.log(e,this.value1) |
| | | }, |
| | |
| | | |
| | | this.fileListto = this.form.annexfiles |
| | | ? this.form.annexfiles |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | : []; |
| | | this.invoicefileListto = this.form.invoicefilesList |
| | | ? this.form.invoicefilesList |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | : []; |
| | | this.form.recordstatus = response.data.recordstatus + ""; |
| | | sessionStorage.removeItem("apiform"); |
| | |
| | | //票据 |
| | | this.fileListto = this.form.annexfiles |
| | | ? this.form.annexfiles |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | : []; |
| | | this.invoicefileListto = this.form.invoicefilesList |
| | | ? this.form.invoicefilesList |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | : []; |
| | | //审核状态 |
| | | this.form.recordstatus = response.data.recordstatus + ""; |
| | | this.form.recordstatus = this.toStatusText(response.data.recordstatus, "-1"); |
| | | let ids = response.data.id; |
| | | let listFundflowparams = { |
| | | fundid: ids, |
| | |
| | | }); |
| | | |
| | | //报销明细 |
| | | getReimbursementdetaillist(ids).then(res => { |
| | | this.rbDetails = res.data; |
| | | //合计费用 |
| | | // if (this.rbDetails.length > 0) this.sumTotalFee(); |
| | | for (let i = 0; i < this.rbDetails.length; i++) { |
| | | this.sumRowFee(this.rbDetails[i]); |
| | | } |
| | | }); |
| | | getReimbursementdetaillist(ids) |
| | | .then(res => { |
| | | this.rbDetails = this.normalizeDetailList(res); |
| | | //合计费用 |
| | | // if (this.rbDetails.length > 0) this.sumTotalFee(); |
| | | for (let i = 0; i < this.rbDetails.length; i++) { |
| | | this.sumRowFee(this.rbDetails[i]); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | this.rbDetails = []; |
| | | console.error("获取报销明细失败,id=" + ids, err); |
| | | }); |
| | | |
| | | //付款信息 |
| | | this.queryParamsPayee.rbid = ids; |
| | |
| | | this.reset(); |
| | | this.open = true; |
| | | const id = ids || this.ids; |
| | | this.checkObj.fundid = id; |
| | | // 重置审批信息,避免上一单的审批结果带入下一单 |
| | | this.checkObj = { |
| | | flowlevelone: null, |
| | | flowconclusion: null, |
| | | fundid: id |
| | | }; |
| | | |
| | | // 连续审批:加载当前用户的待审批列表 |
| | | this.getPendingList(); |
| | | |
| | | getReimbursement(id).then(response => { |
| | | this.form = response.data; |
| | | console.log(this.form.checkstatus, "checkstatus"); |
| | | this.form.checkstatus = this.form.checkstatus.toString(); |
| | | // checkstatus 在库中可为空(预审状态尚未产生时),直接 toString 会抛 TypeError 中断后续取数 |
| | | this.form.checkstatus = this.toStatusText(this.form.checkstatus, "-1"); |
| | | |
| | | //票据 |
| | | this.fileListto = this.form.annexfiles |
| | | ? this.form.annexfiles |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | : []; |
| | | this.invoicefileListto = this.form.invoicefilesList |
| | | ? this.form.invoicefilesList |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | .split(",") |
| | | .map(item => ({ url: item, name: item })) |
| | | : []; |
| | | |
| | | //审核状态 |
| | |
| | | }); |
| | | |
| | | //报销明细 |
| | | getReimbursementdetaillist(ids).then(res => { |
| | | this.rbDetails = res.data; |
| | | console.log(this.rbDetails); |
| | | getReimbursementdetaillist(ids) |
| | | .then(res => { |
| | | this.rbDetails = this.normalizeDetailList(res); |
| | | console.log("报销明细行数:", this.rbDetails.length, this.rbDetails); |
| | | |
| | | //合计费用 |
| | | // if (this.rbDetails.length > 0) this.sumTotalFee(); |
| | | for (let i = 0; i < this.rbDetails.length; i++) { |
| | | this.sumRowFee(this.rbDetails[i]); |
| | | } |
| | | }); |
| | | //合计费用 |
| | | // if (this.rbDetails.length > 0) this.sumTotalFee(); |
| | | for (let i = 0; i < this.rbDetails.length; i++) { |
| | | this.sumRowFee(this.rbDetails[i]); |
| | | } |
| | | }) |
| | | .catch(err => { |
| | | // 取数失败时不再静默:置空并打出原因,避免“接口有数据但页面空白”无从排查 |
| | | this.rbDetails = []; |
| | | console.error("获取报销明细失败,id=" + ids, err); |
| | | }); |
| | | |
| | | //付款信息 |
| | | this.queryParamsPayee.rbid = ids; |
| | |
| | | if (res.code == 200) { |
| | | this.$modal.closeLoading(); |
| | | this.$modal.msgSuccess("提交审核成功!"); |
| | | // 关闭窗口 |
| | | this.$store.dispatch("tagsView/delView", this.$route); |
| | | this.$router.go(-1); |
| | | // 连续审批:询问是否继续审批下一单 |
| | | this.continueOrExit(); |
| | | } else { |
| | | this.$modal.closeLoading(); |
| | | if (res.msg) { |
| | | this.$modal.msgError("操作失误"); |
| | | } else { |
| | | this.$modal.msgError(res.msg); |
| | | } |
| | | this.$modal.msgError(res.msg || "操作失败"); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 获取当前用户待审批列表(用于连续审批) |
| | | getPendingList() { |
| | | listbypowerone({ |
| | | pageNum: 1, |
| | | pageSize: 1000, |
| | | CHECKFLAG: 1 |
| | | }) |
| | | .then(res => { |
| | | this.pendingList = res.rows || []; |
| | | }) |
| | | .catch(() => { |
| | | this.pendingList = []; |
| | | }); |
| | | }, |
| | | |
| | | // 审批完成后:询问是否继续审批下一单 |
| | | continueOrExit() { |
| | | const list = this.pendingList || []; |
| | | const curId = String(this.checkObj.fundid); |
| | | const idx = list.findIndex(item => String(item.id) === curId); |
| | | let next = null; |
| | | if (idx > -1 && idx + 1 < list.length) { |
| | | next = list[idx + 1]; |
| | | } else { |
| | | // 当前已是末位(或从中间开始),取剩余列表中第一条非当前单据 |
| | | next = list.find(item => String(item.id) !== curId) || null; |
| | | } |
| | | |
| | | if (next) { |
| | | this.$confirm("审批已完成,是否继续审批下一张待审批单据?", "提示", { |
| | | confirmButtonText: "继续审批", |
| | | cancelButtonText: "返回列表", |
| | | type: "info" |
| | | }) |
| | | .then(() => { |
| | | // 直接在当前页面加载下一张待审批单据 |
| | | this.handleCheck(next.id); |
| | | // 同步更新地址栏,避免刷新后回到上一单 |
| | | this.$router |
| | | .replace({ |
| | | path: this.$route.path, |
| | | query: { id: next.id, pos: 4 } |
| | | }) |
| | | .catch(() => {}); |
| | | }) |
| | | .catch(() => { |
| | | this.exitToPage(); |
| | | }); |
| | | } else { |
| | | this.$modal.msgSuccess("已无待审批单据,返回列表"); |
| | | this.exitToPage(); |
| | | } |
| | | }, |
| | | |
| | | // 返回列表页 |
| | | exitToPage() { |
| | | this.$store.dispatch("tagsView/delView", this.$route); |
| | | this.$router.go(-1); |
| | | }, |
| | | |
| | | /** 提交保存按钮 */ |
| | | submitForm: debounce(function(data) { |
| | | submitForm: debounce(function (data) { |
| | | // 2026-10-15:保存前附件文件名规范校验(仅新增单据;老单据的历史附件由后端按"只校验本次变更"放行) |
| | | if (!this.checkDetailFileNames()) { |
| | | return; |
| | | } |
| | | |
| | | this.$refs["form"].validate(valid => { |
| | | if (valid) { |
| | | //票据文件处理 |
| | | const addnumber = new Decimal(this.rbPayees.reduce( |
| | | (amount, item) => amount + Number(item.amount), |
| | | 0 |
| | | )); |
| | | console.log(this.form.amountrequested,addnumber); |
| | | //校验请款金额合计是否等于报销单金额 |
| | | if (this.form.amountrequested != addnumber) { |
| | | // const addnumber = new Decimal(this.rbPayees.reduce((amount, item) => amount + Number(item.amount), 0 )); |
| | | // console.log(this.form.amountrequested,addnumber); |
| | | // //校验请款金额合计是否等于报销单金额 |
| | | // if (this.form.amountrequested != addnumber) { |
| | | // this.$message({ |
| | | // message: |
| | | // "报销请款表中合计金额不等于报销单的报销金额,请更正后再保存!!", |
| | | // type: "warning" |
| | | // }); |
| | | // return; |
| | | // } |
| | | |
| | | // 1. 在 reduce 累加时,直接传入字符串给 Decimal,并使用 .plus() 方法进行累加 |
| | | // 初始值设为 new Decimal(0),确保全程高精度运算 |
| | | const addnumber = this.rbPayees.reduce((amount, item) => { |
| | | return amount.plus(new Decimal(item.amount.toString())); |
| | | }, new Decimal(0)); |
| | | |
| | | console.log(this.form.amountrequested, addnumber.toString()); |
| | | |
| | | // 2. 使用 Decimal 的 .eq() 方法进行严格相等比较 |
| | | // 这样既解决了精度问题,也解决了类型转换比较的问题 |
| | | if (!addnumber.eq(this.form.amountrequested)) { |
| | | this.$message({ |
| | | message: |
| | | "报销请款表中合计金额不等于报销单的报销金额,请更正后再保存!!", |
| | | message: "报销请款表中合计金额不等于报销单的报销金额,请更正后再保存!!", |
| | | type: "warning" |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | |
| | | const rbDetails = [...this.rbDetails]; |
| | | const rbPayees = [...this.rbPayees]; |
| | |
| | | if (!tempDetail.persontype) { |
| | | this.$modal.msgWarning( |
| | | "明细表中,第" + |
| | | (k + 1).toString() + |
| | | "行的人员类别未选择,请先选择人员类别后再提交保存!" |
| | | (k + 1).toString() + |
| | | "行的人员类别未选择,请先选择人员类别后再提交保存!" |
| | | ); |
| | | return; |
| | | } |
| | |
| | | if (!tempDetail.personname) { |
| | | this.$modal.msgWarning( |
| | | "明细表中,第" + |
| | | (k + 1).toString() + |
| | | "行的费用人员未选择,请选择费用人员后再提交保存!" |
| | | (k + 1).toString() + |
| | | "行的费用人员未选择,请选择费用人员后再提交保存!" |
| | | ); |
| | | return; |
| | | } |
| | |
| | | if (!tempDetail.starttime) { |
| | | this.$modal.msgWarning( |
| | | "明细表中,第" + |
| | | (k + 1).toString() + |
| | | "行的开始日期未选择,请选择开始日期后再提交保存!" |
| | | (k + 1).toString() + |
| | | "行的开始日期未选择,请选择开始日期后再提交保存!" |
| | | ); |
| | | return; |
| | | } |
| | |
| | | if (!tempDetail.endtime) { |
| | | this.$modal.msgWarning( |
| | | "明细表中,第" + |
| | | (k + 1).toString() + |
| | | "行的结束日期姓名为选择,请选择结束日期后再提交保存!" |
| | | (k + 1).toString() + |
| | | "行的结束日期姓名为选择,请选择结束日期后再提交保存!" |
| | | ); |
| | | return; |
| | | } |
| | |
| | | //保存明细 |
| | | for (let i = 0; i < rbDetails.length; i++) { |
| | | if (rbDetails[i].rbid != null) { |
| | | if (rbDetails[i].persontype == "家属") { |
| | | if (rbDetails[i].persontype == "家属"||rbDetails[i].persontype == "专家") { |
| | | if (this.form.infoid) { |
| | | updateReimbursementdetail(rbDetails[i]); |
| | | } else { |
| | | return this.$message.error( |
| | | `因家属分配要求,请选择捐献者信息` |
| | | "因财务核算的要求,家属或专家的报销,需要选择对应的捐献者!" |
| | | ); |
| | | } |
| | | } else { |
| | |
| | | addReimbursementdetail(rbDetails[i]); |
| | | } else { |
| | | return this.$message.error( |
| | | `因家属分配要求,请选择捐献者信息` |
| | | "因财务核算的要求,家属或专家的报销,需要选择对应的捐献者!" |
| | | ); |
| | | } |
| | | } else { |
| | |
| | | const ids = row.id || this.ids; |
| | | this.$modal |
| | | .confirm("是否确认删除该条记录?") |
| | | .then(function() { |
| | | .then(function () { |
| | | // 删除details内的数据 |
| | | return delReimbursement(ids); |
| | | }) |
| | |
| | | this.getList(); |
| | | this.$modal.msgSuccess("删除成功"); |
| | | }) |
| | | .catch(() => {}); |
| | | .catch(() => { }); |
| | | }, |
| | | |
| | | /** 导出按钮操作 */ |
| | |
| | | this.$download.name(response.msg); |
| | | this.exportLoading = false; |
| | | }) |
| | | .catch(() => {}); |
| | | .catch(() => { }); |
| | | }, |
| | | // 取消提醒 |
| | | unsave() { |
| | |
| | | }, |
| | | // 接受提醒立刻保存 |
| | | Savenow() { |
| | | // 2026-10-15:保存前附件文件名规范校验(仅新增单据;老单据的历史附件由后端按"只校验本次变更"放行) |
| | | if (!this.checkDetailFileNames()) { |
| | | return; |
| | | } |
| | | |
| | | this.Savereminder = false; |
| | | this.Reminderquantity = 0; |
| | | this.totalquantity = 0; |
| | |
| | | }); |
| | | }, |
| | | |
| | | //计算一行合计 |
| | | //计算当前行报销内容费用合计 |
| | | sumRowFee(row) { |
| | | let totalFee = 0; |
| | | if (!isNaN(parseFloat(row.trafficexpense))) { |
| | | totalFee += parseFloat(row.trafficexpense); |
| | | /** |
| | | * 状态类字段转字符串(接口可能返回 null)。 |
| | | * |
| | | * 背景:checkstatus、recordstatus 等在库中可空,早期直接调用 .toString() |
| | | * 会抛 TypeError 并中断其后的取数逻辑,表现为“接口已有数据但明细区空白”。 |
| | | * |
| | | * @param {*} value 原始状态值 |
| | | * @param {string} fallback 为空时的兜底值 |
| | | * @returns {string} |
| | | */ |
| | | toStatusText(value, fallback) { |
| | | if (value === null || value === undefined || value === "") { |
| | | return fallback; |
| | | } |
| | | if (!isNaN(parseFloat(row.cityfee))) { |
| | | totalFee += parseFloat(row.cityfee); |
| | | } |
| | | if (!isNaN(parseFloat(row.hotelexpense))) { |
| | | totalFee += parseFloat(row.hotelexpense); |
| | | } |
| | | if (!isNaN(parseFloat(row.otherexpense))) { |
| | | totalFee += parseFloat(row.otherexpense); |
| | | } |
| | | if (!isNaN(parseFloat(row.foodexpenses))) { |
| | | totalFee += parseFloat(row.foodexpenses); |
| | | } |
| | | if (!isNaN(parseFloat(row.foodallowance))) { |
| | | totalFee += parseFloat(row.foodallowance); |
| | | } |
| | | if (!isNaN(parseFloat(row.otherfeeamount))) { |
| | | totalFee += parseFloat(row.otherfeeamount); |
| | | } |
| | | row.total = totalFee.toFixed(2); |
| | | return String(value); |
| | | }, |
| | | |
| | | /* |
| | | if (!isNaN(parseFloat(this.rbDetails[rowIndex].trafficexpense))) { |
| | | totalFee += parseFloat(this.rbDetails[rowIndex].trafficexpense); |
| | | /** |
| | | * 统一从明细接口响应中取出数组。 |
| | | * |
| | | * 正常为 AjaxResult 的 data 数组;若后端改为分页结构(total + rows), |
| | | * res.data 将不是数组,直接绑定到 el-table 会导致“有数据但不显示”。 |
| | | * |
| | | * @param {Object} res 接口原始响应 |
| | | * @returns {Array} 明细数组(异常时为空数组,不会让表格拿到非数组) |
| | | */ |
| | | normalizeDetailList(res) { |
| | | if (!res) { |
| | | return []; |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[rowIndex].cityfee))) { |
| | | totalFee += parseFloat(this.rbDetails[rowIndex].cityfee); |
| | | if (Array.isArray(res.data)) { |
| | | return res.data; |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[rowIndex].hotelexpense))) { |
| | | totalFee += parseFloat(this.rbDetails[rowIndex].hotelexpense); |
| | | if (Array.isArray(res.rows)) { |
| | | return res.rows; |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[rowIndex].otherexpense))) { |
| | | totalFee += parseFloat(this.rbDetails[rowIndex].otherexpense); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[rowIndex].foodexpenses))) { |
| | | totalFee += parseFloat(this.rbDetails[rowIndex].foodexpenses); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[rowIndex].foodallowance))) { |
| | | totalFee += parseFloat(this.rbDetails[rowIndex].foodallowance); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[rowIndex].otherfeeamount))) { |
| | | totalFee += parseFloat(this.rbDetails[rowIndex].otherfeeamount); |
| | | } |
| | | this.rbDetails[rowIndex].total = parseFloat(totalFee).toFixed(2); |
| | | this.$set(this.rbDetails, rowIndex, this.rbDetails[rowIndex]); |
| | | */ |
| | | return []; |
| | | }, |
| | | |
| | | //计算一行合计 |
| | | //计算当前行报销内容费用合计(金额一律走精确运算) |
| | | sumRowFee(row) { |
| | | const totalFee = moneySum([ |
| | | row.trafficexpense, |
| | | row.cityfee, |
| | | row.hotelexpense, |
| | | row.otherexpense, |
| | | row.foodexpenses, |
| | | row.foodallowance, |
| | | row.otherfeeamount |
| | | ]); |
| | | row.total = moneyRound(totalFee).toFixed(2); |
| | | |
| | | //重新计算整个报销单的合计 |
| | | this.sumTotalFee(); |
| | | }, |
| | | |
| | | //计算费用合计 |
| | | sumTotalFee() { |
| | | let allSum = 0; |
| | | |
| | | //捐献者家属费用 |
| | | if (!isNaN(parseFloat(this.form.donoramount))) { |
| | | allSum += parseFloat(this.form.donoramount); |
| | | } |
| | | |
| | | //报销明细费用 |
| | | try { |
| | | for (let i = 0; i < this.rbDetails.length; i++) { |
| | | /* |
| | | let totalFee = 0; |
| | | //计算当前报销内容费用合计 |
| | | if (!isNaN(parseFloat(this.rbDetails[i].total))) { |
| | | totalFee += parseFloat(this.rbDetails[i].total); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[i].cityfee))) { |
| | | totalFee += parseFloat(this.rbDetails[i].cityfee); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[i].hotelexpense))) { |
| | | totalFee += parseFloat(this.rbDetails[i].hotelexpense); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[i].otherexpense))) { |
| | | totalFee += parseFloat(this.rbDetails[i].otherexpense); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[i].foodexpenses))) { |
| | | totalFee += parseFloat(this.rbDetails[i].foodexpenses); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[i].foodallowance))) { |
| | | totalFee += parseFloat(this.rbDetails[i].foodallowance); |
| | | } |
| | | if (!isNaN(parseFloat(this.rbDetails[i].otherfeeamount))) { |
| | | totalFee += parseFloat(this.rbDetails[i].otherfeeamount); |
| | | } |
| | | this.rbDetails[i].total = parseFloat(totalFee).toFixed(2); |
| | | this.$set(this.rbDetails, i, this.rbDetails[i]); |
| | | */ |
| | | |
| | | if (!isNaN(parseFloat(this.rbDetails[i].total))) { |
| | | allSum += parseFloat(this.rbDetails[i].total); |
| | | } |
| | | } |
| | | |
| | | this.form.amountrequested = allSum.toFixed(2); |
| | | this.form.amountrequested = moneyAdd( |
| | | moneySum(this.rbDetails, "total"), |
| | | this.form.donoramount |
| | | ).toFixed(2); |
| | | this.form.bigstrmoney = this.convertCurrency(this.form.amountrequested); |
| | | } catch {} |
| | | } catch { } |
| | | }, |
| | | |
| | | //金额数字转换成大写 |