✨ 金蝶附件下载(A801-2)前端:票池弹窗/正式结算/付款申请三入口附件查看下载与类型分流
This commit is contained in:
@@ -7,6 +7,15 @@ export const getList = (current, size, params) =>
|
|||||||
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
|
||||||
export const queryInvoicePool = params =>
|
export const queryInvoicePool = params =>
|
||||||
request({ url: `${baseUrl}/invoice-pool`, method: 'get', params });
|
request({ url: `${baseUrl}/invoice-pool`, method: 'get', params });
|
||||||
|
export const downloadInvoicePoolAttachment = invoiceNo =>
|
||||||
|
// 金蝶票池发票文件下载(后端代理 A801-2 流式回源),耗时较普通接口长,放宽到 120s
|
||||||
|
request({
|
||||||
|
url: `${baseUrl}/invoice-pool/attachment/download`,
|
||||||
|
method: 'get',
|
||||||
|
params: { invoiceNo },
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 120000,
|
||||||
|
});
|
||||||
export const getSettlementCandidates = (keyword, receiptId) =>
|
export const getSettlementCandidates = (keyword, receiptId) =>
|
||||||
request({
|
request({
|
||||||
url: `${baseUrl}/settlement-candidates`,
|
url: `${baseUrl}/settlement-candidates`,
|
||||||
|
|||||||
@@ -104,6 +104,24 @@
|
|||||||
<el-tag v-else type="info" size="small">未登记</el-tag>
|
<el-tag v-else type="info" size="small">未登记</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="附件" width="120" fixed="right" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
:underline="false"
|
||||||
|
:disabled="attachmentBusy === row.invoiceNo"
|
||||||
|
@click.stop="viewAttachment(row)"
|
||||||
|
>查看</el-link
|
||||||
|
>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
:underline="false"
|
||||||
|
:disabled="attachmentBusy === row.invoiceNo"
|
||||||
|
@click.stop="downloadAttachment(row)"
|
||||||
|
>下载</el-link
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column v-if="!multiple" label="操作" width="90" fixed="right" align="center">
|
<el-table-column v-if="!multiple" label="操作" width="90" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link type="primary" @click.stop="confirmRows([row])">选择</el-link>
|
<el-link type="primary" @click.stop="confirmRows([row])">选择</el-link>
|
||||||
@@ -126,6 +144,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import {
|
||||||
|
downloadKingdeeInvoiceFile,
|
||||||
|
isKingdeePathAttachment,
|
||||||
|
viewKingdeeInvoiceFile,
|
||||||
|
} from '@/utils/kingdee-attachment';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'KingdeeInvoicePoolDialog',
|
name: 'KingdeeInvoicePoolDialog',
|
||||||
props: {
|
props: {
|
||||||
@@ -156,6 +180,8 @@ export default {
|
|||||||
pendingCursor: null,
|
pendingCursor: null,
|
||||||
// 已浏览页缓存:上一页直接回显不再请求(0 远程调用)
|
// 已浏览页缓存:上一页直接回显不再请求(0 远程调用)
|
||||||
pageCache: {},
|
pageCache: {},
|
||||||
|
// 正在查看/下载附件的发票号(行级 loading 守卫)
|
||||||
|
attachmentBusy: null,
|
||||||
query: this.emptyQuery(),
|
query: this.emptyQuery(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -184,6 +210,50 @@ export default {
|
|||||||
rowKey(row) {
|
rowKey(row) {
|
||||||
return row.invoiceNo || row.id;
|
return row.invoiceNo || row.id;
|
||||||
},
|
},
|
||||||
|
// 票池行附件字段为复数 attachmentsJson,行内首项即发票文件({source,path,name})
|
||||||
|
rowAttachment(row = {}) {
|
||||||
|
const value = row.attachmentsJson;
|
||||||
|
if (!value) return null;
|
||||||
|
let parsed = value;
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(value);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.isArray(parsed) ? parsed[0] || null : parsed || null;
|
||||||
|
},
|
||||||
|
attachmentName(row) {
|
||||||
|
const attachment = this.rowAttachment(row);
|
||||||
|
return attachment?.name || `${row.invoiceNo || '发票'}.pdf`;
|
||||||
|
},
|
||||||
|
async viewAttachment(row) {
|
||||||
|
const attachment = this.rowAttachment(row);
|
||||||
|
if (!isKingdeePathAttachment(attachment)) {
|
||||||
|
this.$message.warning('当前发票暂无可查看附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.attachmentBusy = row.invoiceNo;
|
||||||
|
try {
|
||||||
|
await viewKingdeeInvoiceFile(row.invoiceNo, this.attachmentName(row));
|
||||||
|
} finally {
|
||||||
|
this.attachmentBusy = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async downloadAttachment(row) {
|
||||||
|
const attachment = this.rowAttachment(row);
|
||||||
|
if (!isKingdeePathAttachment(attachment)) {
|
||||||
|
this.$message.warning('当前发票暂无可下载附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.attachmentBusy = row.invoiceNo;
|
||||||
|
try {
|
||||||
|
await downloadKingdeeInvoiceFile(row.invoiceNo, this.attachmentName(row));
|
||||||
|
} finally {
|
||||||
|
this.attachmentBusy = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
handleOpen() {
|
handleOpen() {
|
||||||
this.query = this.emptyQuery();
|
this.query = this.emptyQuery();
|
||||||
this.query.invoiceNo = this.initialInvoiceNo || '';
|
this.query.invoiceNo = this.initialInvoiceNo || '';
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* 金蝶侧发票附件查看/下载公共工具
|
||||||
|
*
|
||||||
|
* 票池发票附件为 {source:'piaozone', path, name}:path 是金蝶侧文件 id(非 http 直链),
|
||||||
|
* 须由后端代理 A801-2 流式回源;OSS 直链附件(url/link/fileUrl)仍由前端直连下载/预览。
|
||||||
|
* 三处入口共用:票池弹窗、正式结算认领发票行、付款申请发票附件行。
|
||||||
|
*/
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { downloadFile } from '@/utils/util';
|
||||||
|
import { downloadInvoicePoolAttachment } from '@/api/payment/invoiceReceipt';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 金蝶侧附件判定:source=piaozone,或有 path 且非 http(s) 直链
|
||||||
|
* @param {Object|Null} attachment 附件对象(attachmentsJson/attachmentJson 行内首项)
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export const isKingdeePathAttachment = attachment => {
|
||||||
|
if (!attachment) return false;
|
||||||
|
if (attachment.source === 'piaozone') return true;
|
||||||
|
return Boolean(attachment.path) && !/^https?:\/\//i.test(attachment.path);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求金蝶文件流:成功返回 Blob;业务失败为 HTTP 200 + R.fail JSON(响应通道未写流时后端即失败),
|
||||||
|
* 按 Content-Type 识别并抛出带 msg 的错误;网络级失败拦截器已提示,标记 silent 避免重复提示
|
||||||
|
* @param {String} invoiceNo 发票号码
|
||||||
|
* @returns {Promise<Blob>}
|
||||||
|
*/
|
||||||
|
const requestKingdeeFile = async invoiceNo => {
|
||||||
|
let res;
|
||||||
|
try {
|
||||||
|
res = await downloadInvoicePoolAttachment(invoiceNo);
|
||||||
|
} catch (error) {
|
||||||
|
throw Object.assign(new Error(error?.message || '附件下载失败'), { silent: true });
|
||||||
|
}
|
||||||
|
const blob = res?.data;
|
||||||
|
if (!(blob instanceof Blob)) {
|
||||||
|
throw new Error('响应不是文件流');
|
||||||
|
}
|
||||||
|
if (blob.type && blob.type.includes('application/json')) {
|
||||||
|
let message = '附件下载失败';
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(await blob.text());
|
||||||
|
message = parsed.msg || parsed.message || message;
|
||||||
|
} catch {
|
||||||
|
// 错误体非 JSON 时按默认提示
|
||||||
|
}
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
return blob;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载金蝶侧发票附件(经 A801-2 后端代理),失败统一 ElMessage 提示
|
||||||
|
* @param {String} invoiceNo 发票号码
|
||||||
|
* @param {String} fileName 下载文件名(缺省按 发票号.pdf)
|
||||||
|
*/
|
||||||
|
export const downloadKingdeeInvoiceFile = async (invoiceNo, fileName) => {
|
||||||
|
if (!invoiceNo) {
|
||||||
|
ElMessage.warning('当前发票缺少发票号码,无法下载金蝶侧附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const blob = await requestKingdeeFile(invoiceNo);
|
||||||
|
downloadFile(blob, fileName || `${invoiceNo}.pdf`);
|
||||||
|
} catch (error) {
|
||||||
|
if (!error?.silent) ElMessage.error(error?.message || '金蝶附件下载失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新窗口预览金蝶侧发票附件:拉取 Blob 转 objectURL(PDF 可预览;OFD 等浏览器不可预览的格式由浏览器触发下载)
|
||||||
|
* @param {String} invoiceNo 发票号码
|
||||||
|
* @param {String} fileName 预览文件名(缺省按 发票号.pdf)
|
||||||
|
*/
|
||||||
|
export const viewKingdeeInvoiceFile = async (invoiceNo, fileName) => {
|
||||||
|
if (!invoiceNo) {
|
||||||
|
ElMessage.warning('当前发票缺少发票号码,无法预览金蝶侧附件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const blob = await requestKingdeeFile(invoiceNo);
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
window.open(url, '_blank');
|
||||||
|
// 延迟释放 objectURL:立即回收可能导致新窗口尚未加载完成即失效
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
||||||
|
} catch (error) {
|
||||||
|
if (!error?.silent) ElMessage.error(error?.message || '金蝶附件预览失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -237,8 +237,18 @@
|
|||||||
<el-table-column label="操作" width="220" fixed="right" align="center">
|
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<div class="payment-form-page__invoice-actions">
|
<div class="payment-form-page__invoice-actions">
|
||||||
<el-link type="primary" @click="viewInvoiceAttachment(row)">查看</el-link>
|
<el-link
|
||||||
<el-link type="primary" @click="downloadInvoiceAttachment(row)">下载</el-link>
|
type="primary"
|
||||||
|
:disabled="kingdeeAttachmentBusy"
|
||||||
|
@click="viewInvoiceAttachment(row)"
|
||||||
|
>查看</el-link
|
||||||
|
>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
:disabled="kingdeeAttachmentBusy"
|
||||||
|
@click="downloadInvoiceAttachment(row)"
|
||||||
|
>下载</el-link
|
||||||
|
>
|
||||||
<el-link
|
<el-link
|
||||||
v-if="!readonly"
|
v-if="!readonly"
|
||||||
type="danger"
|
type="danger"
|
||||||
@@ -608,6 +618,11 @@ import {
|
|||||||
} from '@/api/vehicle/customer-archive';
|
} from '@/api/vehicle/customer-archive';
|
||||||
import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer';
|
import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer';
|
||||||
import { downloadFileByUrl } from '@/utils/util';
|
import { downloadFileByUrl } from '@/utils/util';
|
||||||
|
import {
|
||||||
|
downloadKingdeeInvoiceFile,
|
||||||
|
isKingdeePathAttachment,
|
||||||
|
viewKingdeeInvoiceFile,
|
||||||
|
} from '@/utils/kingdee-attachment';
|
||||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||||
|
|
||||||
const attachmentViewerPlugins = [
|
const attachmentViewerPlugins = [
|
||||||
@@ -688,6 +703,8 @@ export default {
|
|||||||
form: emptyForm(),
|
form: emptyForm(),
|
||||||
amountSyncing: false,
|
amountSyncing: false,
|
||||||
transferPayload: null,
|
transferPayload: null,
|
||||||
|
// 金蝶侧发票附件查看/下载进行中(行组级 loading 守卫)
|
||||||
|
kingdeeAttachmentBusy: false,
|
||||||
referenceRows: [],
|
referenceRows: [],
|
||||||
paymentRecords: [],
|
paymentRecords: [],
|
||||||
referenceVisible: false,
|
referenceVisible: false,
|
||||||
@@ -1390,8 +1407,19 @@ export default {
|
|||||||
this.attachmentFileName(this.invoiceAttachment(row)) || `${row.invoiceNo || '发票'}.pdf`
|
this.attachmentFileName(this.invoiceAttachment(row)) || `${row.invoiceNo || '发票'}.pdf`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
viewInvoiceAttachment(row) {
|
async viewInvoiceAttachment(row) {
|
||||||
const attachment = this.invoiceAttachment(row);
|
const attachment = this.invoiceAttachment(row);
|
||||||
|
// 金蝶侧附件(path 型)经后端代理 A801-2 预览,OSS 直链维持本地预览组件
|
||||||
|
if (isKingdeePathAttachment(attachment)) {
|
||||||
|
if (this.kingdeeAttachmentBusy) return;
|
||||||
|
this.kingdeeAttachmentBusy = true;
|
||||||
|
try {
|
||||||
|
await viewKingdeeInvoiceFile(row.invoiceNo, this.invoiceAttachmentName(row));
|
||||||
|
} finally {
|
||||||
|
this.kingdeeAttachmentBusy = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!this.attachmentFileUrl(attachment)) {
|
if (!this.attachmentFileUrl(attachment)) {
|
||||||
this.$message.warning('当前发票暂无可查看附件');
|
this.$message.warning('当前发票暂无可查看附件');
|
||||||
return;
|
return;
|
||||||
@@ -1401,8 +1429,20 @@ export default {
|
|||||||
[attachment]
|
[attachment]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
downloadInvoiceAttachment(row) {
|
async downloadInvoiceAttachment(row) {
|
||||||
const url = this.invoiceAttachmentUrl(row);
|
const attachment = this.invoiceAttachment(row);
|
||||||
|
// 金蝶侧附件(path 型)经后端代理 A801-2 下载,OSS 直链维持前端直连下载
|
||||||
|
if (isKingdeePathAttachment(attachment)) {
|
||||||
|
if (this.kingdeeAttachmentBusy) return;
|
||||||
|
this.kingdeeAttachmentBusy = true;
|
||||||
|
try {
|
||||||
|
await downloadKingdeeInvoiceFile(row.invoiceNo, this.invoiceAttachmentName(row));
|
||||||
|
} finally {
|
||||||
|
this.kingdeeAttachmentBusy = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const url = this.attachmentFileUrl(attachment);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
this.$message.warning('当前发票暂无可下载附件');
|
this.$message.warning('当前发票暂无可下载附件');
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -403,8 +403,18 @@
|
|||||||
<el-table-column label="操作" width="220" align="center">
|
<el-table-column label="操作" width="220" align="center">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<div class="formal-editor__links">
|
<div class="formal-editor__links">
|
||||||
<el-link type="primary" @click="viewInvoiceAttachment(row)">查看</el-link>
|
<el-link
|
||||||
<el-link type="primary" @click="downloadInvoiceAttachment(row)">下载</el-link>
|
type="primary"
|
||||||
|
:disabled="kingdeeAttachmentBusy"
|
||||||
|
@click="viewInvoiceAttachment(row)"
|
||||||
|
>查看</el-link
|
||||||
|
>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
:disabled="kingdeeAttachmentBusy"
|
||||||
|
@click="downloadInvoiceAttachment(row)"
|
||||||
|
>下载</el-link
|
||||||
|
>
|
||||||
<el-link v-if="editable" type="danger" @click="removeInvoice($index)">删除</el-link>
|
<el-link v-if="editable" type="danger" @click="removeInvoice($index)">删除</el-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -915,6 +925,11 @@ import { h } from 'vue';
|
|||||||
import { InfoFilled } from '@element-plus/icons-vue';
|
import { InfoFilled } from '@element-plus/icons-vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { downloadFileByUrl } from '@/utils/util';
|
import { downloadFileByUrl } from '@/utils/util';
|
||||||
|
import {
|
||||||
|
downloadKingdeeInvoiceFile,
|
||||||
|
isKingdeePathAttachment,
|
||||||
|
viewKingdeeInvoiceFile,
|
||||||
|
} from '@/utils/kingdee-attachment';
|
||||||
import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/main.vue';
|
import KingdeeInvoicePoolDialog from '@/components/kingdee-invoice-pool-dialog/main.vue';
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
|
|
||||||
@@ -950,6 +965,8 @@ export default {
|
|||||||
invoices: [],
|
invoices: [],
|
||||||
invoiceClaimNo: '',
|
invoiceClaimNo: '',
|
||||||
invoiceClaimLoading: false,
|
invoiceClaimLoading: false,
|
||||||
|
// 金蝶侧发票附件查看/下载进行中(行组级 loading 守卫)
|
||||||
|
kingdeeAttachmentBusy: false,
|
||||||
invoiceClaimDialog: {
|
invoiceClaimDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
selected: [],
|
selected: [],
|
||||||
@@ -2293,12 +2310,36 @@ export default {
|
|||||||
const attachment = this.invoiceAttachment(row);
|
const attachment = this.invoiceAttachment(row);
|
||||||
return attachment.originalName || attachment.name || `${row.invoiceNo || '发票'}.pdf`;
|
return attachment.originalName || attachment.name || `${row.invoiceNo || '发票'}.pdf`;
|
||||||
},
|
},
|
||||||
viewInvoiceAttachment(row) {
|
async viewInvoiceAttachment(row) {
|
||||||
|
const attachment = this.invoiceAttachment(row);
|
||||||
|
// 金蝶侧附件(path 型)经后端代理 A801-2 预览,OSS 直链维持 window.open
|
||||||
|
if (isKingdeePathAttachment(attachment)) {
|
||||||
|
if (this.kingdeeAttachmentBusy) return;
|
||||||
|
this.kingdeeAttachmentBusy = true;
|
||||||
|
try {
|
||||||
|
await viewKingdeeInvoiceFile(row.invoiceNo, this.invoiceAttachmentName(row));
|
||||||
|
} finally {
|
||||||
|
this.kingdeeAttachmentBusy = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
const url = this.invoiceAttachmentUrl(row);
|
const url = this.invoiceAttachmentUrl(row);
|
||||||
if (!url) return this.$message.warning('当前发票暂无可查看附件');
|
if (!url) return this.$message.warning('当前发票暂无可查看附件');
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
},
|
},
|
||||||
downloadInvoiceAttachment(row) {
|
async downloadInvoiceAttachment(row) {
|
||||||
|
const attachment = this.invoiceAttachment(row);
|
||||||
|
// 金蝶侧附件(path 型)经后端代理 A801-2 下载,OSS 直链维持前端直连下载
|
||||||
|
if (isKingdeePathAttachment(attachment)) {
|
||||||
|
if (this.kingdeeAttachmentBusy) return;
|
||||||
|
this.kingdeeAttachmentBusy = true;
|
||||||
|
try {
|
||||||
|
await downloadKingdeeInvoiceFile(row.invoiceNo, this.invoiceAttachmentName(row));
|
||||||
|
} finally {
|
||||||
|
this.kingdeeAttachmentBusy = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
const url = this.invoiceAttachmentUrl(row);
|
const url = this.invoiceAttachmentUrl(row);
|
||||||
if (!url) return this.$message.warning('当前发票暂无可下载附件');
|
if (!url) return this.$message.warning('当前发票暂无可下载附件');
|
||||||
downloadFileByUrl(url, this.invoiceAttachmentName(row));
|
downloadFileByUrl(url, this.invoiceAttachmentName(row));
|
||||||
|
|||||||
Reference in New Issue
Block a user