Merge: feat(报销/拨款) 发票明细与附件双向联动

This commit is contained in:
2026-05-08 17:49:47 +08:00
2 changed files with 60 additions and 29 deletions

View File

@@ -54,8 +54,9 @@
<!-- 拨款单据附件OCR触发区 --> <!-- 拨款单据附件OCR触发区 -->
<el-form-item label="拨款单据附件" prop="accessoryApplyIds"> <el-form-item label="拨款单据附件" prop="accessoryApplyIds">
<file-upload v-model="form.accessoryApplyIds" :limit="50" :file-size="50" <file-upload v-model="form.accessoryApplyIds" :limit="50" :file-size="50"
:file-type="['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']" multiple /> :file-type="['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']" multiple
<div class="hint-text">上传发票收据付款截图等支持 PDF/图片上传后自动识别</div> @delete="onFileDelete" />
<div class="hint-text">上传发票收据付款截图等支持 PDF/图片上传后自动识别明细</div>
</el-form-item> </el-form-item>
<!-- OCR 识别中提示 --> <!-- OCR 识别中提示 -->
@@ -67,9 +68,9 @@
<!-- 发票明细条目表 --> <!-- 发票明细条目表 -->
<div class="block-title"> <div class="block-title">
拨款明细 拨款明细
<span class="block-title-hint">上传发票后自动填充也可手动添加</span> <span class="block-title-hint">上传发票后自动识别删除条目将同步移除对应文件</span>
</div> </div>
<div class="invoice-table"> <div class="invoice-table" v-if="invoiceItems.length">
<div class="invoice-table-header"> <div class="invoice-table-header">
<span class="col-reason">事由说明</span> <span class="col-reason">事由说明</span>
<span class="col-amount">金额</span> <span class="col-amount">金额</span>
@@ -95,8 +96,7 @@
</div> </div>
</div> </div>
<div class="invoice-table-footer"> <div class="invoice-table-footer">
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="addInvoiceItem">添加条目</el-button> <span class="total-hint">
<span class="total-hint" v-if="invoiceItems.length">
合计<b>¥{{ invoiceTotalFormatted }}</b>已自动更新拨款总金额 合计<b>¥{{ invoiceTotalFormatted }}</b>已自动更新拨款总金额
</span> </span>
</div> </div>
@@ -350,23 +350,27 @@ export default {
try { try {
const res = await ocrAppropriationInvoice(ossId) const res = await ocrAppropriationInvoice(ossId)
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
const { items, totalAmount } = res.data const { items, totalAmount, sellerName, invoiceDate, invoiceType } = res.data
// 拼接发票头部信息作为事由前缀:发票类型 · 销售方 · 开票日期
const prefix = [invoiceType, sellerName, invoiceDate].filter(Boolean).join(' · ')
if (items && items.length) { if (items && items.length) {
const startIdx = this.invoiceItems.length const startIdx = this.invoiceItems.length
items.forEach((item, i) => { items.forEach((item, i) => {
const reason = [prefix, item.itemName].filter(Boolean).join(' / ')
this.invoiceItems.push({ this.invoiceItems.push({
ossId: Number(ossId), ossId: Number(ossId),
itemName: item.itemName || '', itemName: item.itemName || '',
reason: item.itemName || '', reason,
amount: item.amount || 0, amount: item.amount || 0,
sortNo: startIdx + i sortNo: startIdx + i
}) })
}) })
} else if (totalAmount) { } else if (totalAmount) {
// 没有明细时用总金额创建一条,事由取发票头部信息
this.invoiceItems.push({ this.invoiceItems.push({
ossId: Number(ossId), ossId: Number(ossId),
itemName: '', itemName: sellerName || '',
reason: '', reason: prefix || '',
amount: totalAmount, amount: totalAmount,
sortNo: this.invoiceItems.length sortNo: this.invoiceItems.length
}) })
@@ -382,12 +386,24 @@ export default {
} }
}, },
addInvoiceItem () { removeInvoiceItem (idx) {
this.invoiceItems.push({ ossId: null, itemName: '', reason: '', amount: 0, sortNo: this.invoiceItems.length }) const item = this.invoiceItems[idx]
if (item.ossId) {
// 删除该文件下所有条目
const ossId = String(item.ossId)
this.invoiceItems = this.invoiceItems.filter(it => String(it.ossId) !== ossId)
// 从附件 CSV 中移除该文件
const ids = (this.form.accessoryApplyIds || '').split(',').filter(id => id && id !== ossId)
this.form.accessoryApplyIds = ids.join(',')
} else {
this.invoiceItems.splice(idx, 1)
}
this.recalcTotal()
}, },
removeInvoiceItem (idx) { onFileDelete (deletedFile) {
this.invoiceItems.splice(idx, 1) const ossId = String(deletedFile.ossId)
this.invoiceItems = this.invoiceItems.filter(item => String(item.ossId) !== ossId)
this.recalcTotal() this.recalcTotal()
}, },

View File

@@ -54,8 +54,9 @@
<!-- 报销单据附件OCR触发区 --> <!-- 报销单据附件OCR触发区 -->
<el-form-item label="报销单据附件" prop="accessoryApplyIds"> <el-form-item label="报销单据附件" prop="accessoryApplyIds">
<file-upload v-model="form.accessoryApplyIds" :limit="200" :file-size="50" <file-upload v-model="form.accessoryApplyIds" :limit="200" :file-size="50"
:file-type="['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']" multiple /> :file-type="['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx']" multiple
<div class="hint-text">上传发票收据付款截图等支持 PDF/图片上传后自动识别</div> @delete="onFileDelete" />
<div class="hint-text">上传发票收据付款截图等支持 PDF/图片上传后自动识别明细</div>
</el-form-item> </el-form-item>
<!-- OCR 识别中提示 --> <!-- OCR 识别中提示 -->
@@ -67,9 +68,9 @@
<!-- 发票明细条目表 --> <!-- 发票明细条目表 -->
<div class="block-title"> <div class="block-title">
发票明细 发票明细
<span class="block-title-hint">上传发票后自动填充也可手动添加</span> <span class="block-title-hint">上传发票后自动识别删除条目将同步移除对应文件</span>
</div> </div>
<div class="invoice-table"> <div class="invoice-table" v-if="invoiceItems.length">
<div class="invoice-table-header"> <div class="invoice-table-header">
<span class="col-reason">事由说明</span> <span class="col-reason">事由说明</span>
<span class="col-amount">金额</span> <span class="col-amount">金额</span>
@@ -95,8 +96,7 @@
</div> </div>
</div> </div>
<div class="invoice-table-footer"> <div class="invoice-table-footer">
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="addInvoiceItem">添加条目</el-button> <span class="total-hint">
<span class="total-hint" v-if="invoiceItems.length">
合计<b>¥{{ invoiceTotalFormatted }}</b>已自动更新报销总金额 合计<b>¥{{ invoiceTotalFormatted }}</b>已自动更新报销总金额
</span> </span>
</div> </div>
@@ -325,24 +325,27 @@ export default {
try { try {
const res = await ocrReimburseInvoice(ossId) const res = await ocrReimburseInvoice(ossId)
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
const { items, totalAmount } = res.data const { items, totalAmount, sellerName, invoiceDate, invoiceType } = res.data
// 拼接发票头部信息作为事由前缀:发票类型 · 销售方 · 开票日期
const prefix = [invoiceType, sellerName, invoiceDate].filter(Boolean).join(' · ')
if (items && items.length) { if (items && items.length) {
const startIdx = this.invoiceItems.length const startIdx = this.invoiceItems.length
items.forEach((item, i) => { items.forEach((item, i) => {
const reason = [prefix, item.itemName].filter(Boolean).join(' / ')
this.invoiceItems.push({ this.invoiceItems.push({
ossId: Number(ossId), ossId: Number(ossId),
itemName: item.itemName || '', itemName: item.itemName || '',
reason: item.itemName || '', reason,
amount: item.amount || 0, amount: item.amount || 0,
sortNo: startIdx + i sortNo: startIdx + i
}) })
}) })
} else if (totalAmount) { } else if (totalAmount) {
// 没有明细时用总金额创建一条 // 没有明细时用总金额创建一条,事由取发票头部信息
this.invoiceItems.push({ this.invoiceItems.push({
ossId: Number(ossId), ossId: Number(ossId),
itemName: '', itemName: sellerName || '',
reason: '', reason: prefix || '',
amount: totalAmount, amount: totalAmount,
sortNo: this.invoiceItems.length sortNo: this.invoiceItems.length
}) })
@@ -358,12 +361,24 @@ export default {
} }
}, },
addInvoiceItem () { removeInvoiceItem (idx) {
this.invoiceItems.push({ ossId: null, itemName: '', reason: '', amount: 0, sortNo: this.invoiceItems.length }) const item = this.invoiceItems[idx]
if (item.ossId) {
// 删除该文件下所有条目
const ossId = String(item.ossId)
this.invoiceItems = this.invoiceItems.filter(it => String(it.ossId) !== ossId)
// 从附件 CSV 中移除该文件
const ids = (this.form.accessoryApplyIds || '').split(',').filter(id => id && id !== ossId)
this.form.accessoryApplyIds = ids.join(',')
} else {
this.invoiceItems.splice(idx, 1)
}
this.recalcTotal()
}, },
removeInvoiceItem (idx) { onFileDelete (deletedFile) {
this.invoiceItems.splice(idx, 1) const ossId = String(deletedFile.ossId)
this.invoiceItems = this.invoiceItems.filter(item => String(item.ossId) !== ossId)
this.recalcTotal() this.recalcTotal()
}, },