Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b077ba5be4 | |||
| 4294c92e43 | |||
| b840530fe9 | |||
| e6a1370de2 | |||
| 65296618a7 | |||
| c4ca347f0e | |||
| b4ecc90470 |
@@ -14,21 +14,53 @@
|
|||||||
end-placeholder="结束日期"
|
end-placeholder="结束日期"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="销方(开票方税号)">
|
<el-form-item label="开票单位">
|
||||||
<el-input
|
<el-input v-model="query.salerName" clearable @keyup.enter="handleQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="受票单位">
|
||||||
|
<el-input v-model="query.buyerName" clearable @keyup.enter="handleQuery" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开票单位(销方)">
|
||||||
|
<el-select
|
||||||
v-model="query.salerTaxNo"
|
v-model="query.salerTaxNo"
|
||||||
clearable
|
clearable
|
||||||
style="width: 180px"
|
filterable
|
||||||
|
remote
|
||||||
|
reserve-keyword
|
||||||
|
:remote-method="keyword => searchCustomers(keyword, 'saler')"
|
||||||
|
:loading="customerLoading.saler"
|
||||||
|
placeholder="输入名称搜索或直接粘贴税号"
|
||||||
|
style="width: 220px"
|
||||||
@keyup.enter="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
/>
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in customerOptions.saler"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="购方(收票方税号)">
|
<el-form-item label="受票单位(购方)">
|
||||||
<el-input
|
<el-select
|
||||||
v-model="query.buyerTaxNo"
|
v-model="query.buyerTaxNo"
|
||||||
clearable
|
clearable
|
||||||
style="width: 180px"
|
filterable
|
||||||
|
remote
|
||||||
|
reserve-keyword
|
||||||
|
:remote-method="keyword => searchCustomers(keyword, 'buyer')"
|
||||||
|
:loading="customerLoading.buyer"
|
||||||
|
placeholder="输入名称搜索或直接粘贴税号"
|
||||||
|
style="width: 220px"
|
||||||
@keyup.enter="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
/>
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in customerOptions.buyer"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="发票类型">
|
<el-form-item label="发票类型">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -46,6 +78,18 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="认领状态">
|
||||||
|
<el-select v-model="query.claimed" clearable placeholder="不限" style="width: 120px">
|
||||||
|
<el-option label="未认领" :value="false" />
|
||||||
|
<el-option label="已认领" :value="true" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="登记状态">
|
||||||
|
<el-select v-model="query.registered" clearable placeholder="不限" style="width: 120px">
|
||||||
|
<el-option label="未登记" :value="false" />
|
||||||
|
<el-option label="已登记" :value="true" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button :disabled="loading" @click="resetQuery">重置</el-button>
|
<el-button :disabled="loading" @click="resetQuery">重置</el-button>
|
||||||
<el-button type="primary" :disabled="loading" @click="handleQuery">查询</el-button>
|
<el-button type="primary" :disabled="loading" @click="handleQuery">查询</el-button>
|
||||||
@@ -91,7 +135,7 @@
|
|||||||
<el-table-column label="剩余可用金额" min-width="130" align="right">
|
<el-table-column label="剩余可用金额" min-width="130" align="right">
|
||||||
<template #default="{ row }">{{ formatMoney(row.availableTaxAmount) }}</template>
|
<template #default="{ row }">{{ formatMoney(row.availableTaxAmount) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="kingdeeBillNo" label="金蝶单据号" min-width="210" show-overflow-tooltip />
|
<!--<el-table-column prop="kingdeeBillNo" label="金蝶单据号" min-width="210" show-overflow-tooltip />-->
|
||||||
<el-table-column label="认领状态" min-width="100" align="center">
|
<el-table-column label="认领状态" min-width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag v-if="row.claimed" type="danger" size="small">已认领</el-tag>
|
<el-tag v-if="row.claimed" type="danger" size="small">已认领</el-tag>
|
||||||
@@ -129,12 +173,17 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="kingdee-pool-dialog__pager">
|
<div class="kingdee-pool-dialog__pager">
|
||||||
<span class="kingdee-pool-dialog__pager-info">第 {{ page.current }} 页</span>
|
<span class="kingdee-pool-dialog__pager-tip">按名称检索范围为已同步票,未找到请改用发票号码或税号精确查找</span>
|
||||||
<el-button :disabled="page.current <= 1 || loading" @click="prevPage">上一页</el-button>
|
<el-pagination
|
||||||
<el-button :disabled="!page.hasMore || loading" @click="nextPage">下一页</el-button>
|
v-model:current-page="page.current"
|
||||||
<el-select v-model="page.size" style="width: 110px" @change="handleSizeChange">
|
v-model:page-size="page.size"
|
||||||
<el-option v-for="n in [10, 20, 50, 100]" :key="n" :label="`${n}条/页`" :value="n" />
|
:total="page.total"
|
||||||
</el-select>
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:disabled="loading"
|
||||||
|
@current-change="load"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="visible = false">取消</el-button>
|
<el-button @click="visible = false">取消</el-button>
|
||||||
@@ -144,6 +193,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import request from '@/axios';
|
||||||
|
import { getList as getCustomerList } from '@/api/vehicle/customer-archive';
|
||||||
import {
|
import {
|
||||||
downloadKingdeeInvoiceFile,
|
downloadKingdeeInvoiceFile,
|
||||||
isKingdeePathAttachment,
|
isKingdeePathAttachment,
|
||||||
@@ -164,7 +215,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
// 与后端 BfiConstant.INVOICE_TYPE_NAMES 对齐:value 为发票类型编码,label 为中文名
|
// 与后端 BfiConstant.INVOICE_TYPE_NAMES 对齐:value 为发票类型编码(镜像按 invoice_type_code 精确匹配)
|
||||||
// 选票场景只开放数电票(普票/专票)
|
// 选票场景只开放数电票(普票/专票)
|
||||||
invoiceTypeOptions: [
|
invoiceTypeOptions: [
|
||||||
{ value: '26', label: '数电发票(普通发票)' },
|
{ value: '26', label: '数电发票(普通发票)' },
|
||||||
@@ -173,15 +224,12 @@ export default {
|
|||||||
rows: [],
|
rows: [],
|
||||||
selected: [],
|
selected: [],
|
||||||
current: null,
|
current: null,
|
||||||
page: { current: 1, size: 10, hasMore: false },
|
page: { current: 1, size: 10, total: 0 },
|
||||||
// 续扫游标(服务端过滤补页后翻页须按游标对齐,防漏行/重行)
|
|
||||||
nextCursor: null,
|
|
||||||
// 下一页请求待回传的游标:nextPage 时暂存,load 成功或失败后清空
|
|
||||||
pendingCursor: null,
|
|
||||||
// 已浏览页缓存:上一页直接回显不再请求(0 远程调用)
|
|
||||||
pageCache: {},
|
|
||||||
// 正在查看/下载附件的发票号(行级 loading 守卫)
|
// 正在查看/下载附件的发票号(行级 loading 守卫)
|
||||||
attachmentBusy: null,
|
attachmentBusy: null,
|
||||||
|
// 销/购方客商下拉:远程按名称搜客商档案,选中带出税号查询;手动输入的裸税号直接作为值过滤
|
||||||
|
customerOptions: { saler: [], buyer: [] },
|
||||||
|
customerLoading: { saler: false, buyer: false },
|
||||||
query: this.emptyQuery(),
|
query: this.emptyQuery(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -201,14 +249,18 @@ export default {
|
|||||||
invoiceNo: '',
|
invoiceNo: '',
|
||||||
// 默认查近一个月,随请求传后端并在页面回显
|
// 默认查近一个月,随请求传后端并在页面回显
|
||||||
invoiceDateRange: [this.$dayjs().subtract(1, 'month').format('YYYY-MM-DD'), this.$dayjs().format('YYYY-MM-DD')],
|
invoiceDateRange: [this.$dayjs().subtract(1, 'month').format('YYYY-MM-DD'), this.$dayjs().format('YYYY-MM-DD')],
|
||||||
|
salerName: '',
|
||||||
|
buyerName: '',
|
||||||
salerTaxNo: '',
|
salerTaxNo: '',
|
||||||
buyerTaxNo: '',
|
buyerTaxNo: '',
|
||||||
invoiceType: '',
|
invoiceType: '',
|
||||||
|
claimed: null,
|
||||||
|
registered: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// 远程实时查询的票未入本地镜像,id 恒空,用发票号码兜底作为行主键
|
// 镜像行主键为 id,回源入库前的响应仍可能无 id,用发票号码兜底
|
||||||
rowKey(row) {
|
rowKey(row) {
|
||||||
return row.invoiceNo || row.id;
|
return row.id || row.invoiceNo;
|
||||||
},
|
},
|
||||||
// 票池行附件字段为复数 attachmentsJson,行内首项即发票文件({source,path,name})
|
// 票池行附件字段为复数 attachmentsJson,行内首项即发票文件({source,path,name})
|
||||||
rowAttachment(row = {}) {
|
rowAttachment(row = {}) {
|
||||||
@@ -261,46 +313,52 @@ export default {
|
|||||||
this.selected = [];
|
this.selected = [];
|
||||||
this.$refs.poolTable?.clearSelection();
|
this.$refs.poolTable?.clearSelection();
|
||||||
this.page.current = 1;
|
this.page.current = 1;
|
||||||
this.resetScanState();
|
|
||||||
this.load();
|
this.load();
|
||||||
},
|
},
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.page.current = 1;
|
this.page.current = 1;
|
||||||
this.resetScanState();
|
|
||||||
this.load();
|
this.load();
|
||||||
},
|
},
|
||||||
handleSizeChange() {
|
handleSizeChange() {
|
||||||
this.page.current = 1;
|
this.page.current = 1;
|
||||||
this.resetScanState();
|
|
||||||
this.load();
|
|
||||||
},
|
|
||||||
prevPage() {
|
|
||||||
// 已浏览页直接回显缓存,不再请求
|
|
||||||
const cached = this.pageCache[this.page.current - 1];
|
|
||||||
if (cached) {
|
|
||||||
this.page.current -= 1;
|
|
||||||
this.rows = cached.rows;
|
|
||||||
this.page.hasMore = cached.hasMore;
|
|
||||||
this.nextCursor = cached.nextCursor;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.page.current -= 1;
|
|
||||||
this.load();
|
|
||||||
},
|
|
||||||
nextPage() {
|
|
||||||
this.pendingCursor = this.nextCursor;
|
|
||||||
this.page.current += 1;
|
|
||||||
this.load();
|
this.load();
|
||||||
},
|
},
|
||||||
resetQuery() {
|
resetQuery() {
|
||||||
this.query = this.emptyQuery();
|
this.query = this.emptyQuery();
|
||||||
this.handleQuery();
|
this.handleQuery();
|
||||||
},
|
},
|
||||||
// 新查询/换页容量/重新打开时清空游标与已浏览页缓存,重新锚定扫描
|
// 客商名称远程搜索:含 18 位社会信用代码特征(数字+大写字母)时按裸税号直查;
|
||||||
resetScanState() {
|
// 否则搜客商档案,取客商发票信息(blade_customer_invoice_info)的抬头/税号作选项——票池税号是开票税号,非统一信用代码
|
||||||
this.nextCursor = null;
|
async searchCustomers(keyword, side) {
|
||||||
this.pendingCursor = null;
|
const text = String(keyword || '').trim();
|
||||||
this.pageCache = {};
|
if (!text) return;
|
||||||
|
if (/^[0-9A-Z]{15,20}$/.test(text)) {
|
||||||
|
this.customerOptions[side] = [{ label: `${text}(手输税号)`, value: text }];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.customerLoading[side] = true;
|
||||||
|
try {
|
||||||
|
const data = this.unwrap(await getCustomerList(1, 20, { fullName: text }));
|
||||||
|
const customers = (data.records || []).filter(item => item.id);
|
||||||
|
if (!customers.length) {
|
||||||
|
this.customerOptions[side] = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 列表接口不带发票信息,逐户取 detail(detail 组装 invoices,每户一张发票信息)
|
||||||
|
const options = await Promise.all(
|
||||||
|
customers.map(async customer => {
|
||||||
|
const detail = this.unwrap(await request({ url: '/blade-transport/customer-archive/detail', method: 'get', params: { id: customer.id } }));
|
||||||
|
return (detail?.invoices || [])
|
||||||
|
.filter(invoice => invoice.taxNo)
|
||||||
|
.map(invoice => ({ label: `${invoice.invoiceTitle || customer.fullName}(${invoice.taxNo})`, value: invoice.taxNo }));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.customerOptions[side] = options.flat().filter(
|
||||||
|
(option, index, list) => list.findIndex(item => item.value === option.value) === index
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.customerLoading[side] = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
unwrap(response) {
|
unwrap(response) {
|
||||||
const body = response?.data || response || {};
|
const body = response?.data || response || {};
|
||||||
@@ -308,34 +366,26 @@ export default {
|
|||||||
},
|
},
|
||||||
async load() {
|
async load() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
// 请求前快照页码:prev/next 会先改写 current,远程失败时回滚避免停留在错页
|
|
||||||
const lastRequested = this.page.current;
|
|
||||||
const cursor = this.pendingCursor;
|
|
||||||
this.pendingCursor = null;
|
|
||||||
try {
|
try {
|
||||||
const range = this.query.invoiceDateRange || [];
|
const range = this.query.invoiceDateRange || [];
|
||||||
const data = this.unwrap(
|
const data = this.unwrap(
|
||||||
await this.queryApi({
|
await this.queryApi({
|
||||||
current: this.page.current,
|
current: this.page.current,
|
||||||
size: this.page.size,
|
size: this.page.size,
|
||||||
cursor: cursor || undefined,
|
|
||||||
invoiceNo: this.query.invoiceNo || undefined,
|
invoiceNo: this.query.invoiceNo || undefined,
|
||||||
invoiceDateStart: range[0] || undefined,
|
invoiceDateStart: range[0] || undefined,
|
||||||
invoiceDateEnd: range[1] || undefined,
|
invoiceDateEnd: range[1] || undefined,
|
||||||
|
salerName: this.query.salerName || undefined,
|
||||||
|
buyerName: this.query.buyerName || undefined,
|
||||||
salerTaxNo: this.query.salerTaxNo || undefined,
|
salerTaxNo: this.query.salerTaxNo || undefined,
|
||||||
buyerTaxNo: this.query.buyerTaxNo || undefined,
|
buyerTaxNo: this.query.buyerTaxNo || undefined,
|
||||||
invoiceType: this.query.invoiceType || undefined,
|
invoiceType: this.query.invoiceType || undefined,
|
||||||
|
claimed: this.query.claimed ?? undefined,
|
||||||
|
registered: this.query.registered ?? undefined,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.rows = data.records || [];
|
this.rows = data.records || [];
|
||||||
this.page.hasMore = !!data.hasMore;
|
this.page.total = Number(data.total || 0);
|
||||||
this.nextCursor = data.nextCursor || null;
|
|
||||||
this.pageCache[this.page.current] = { rows: this.rows, hasMore: this.page.hasMore, nextCursor: this.nextCursor };
|
|
||||||
} catch (error) {
|
|
||||||
if (this.page.current !== lastRequested) {
|
|
||||||
this.page.current = lastRequested;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
@@ -373,8 +423,12 @@ export default {
|
|||||||
.kingdee-pool-dialog__pager {
|
.kingdee-pool-dialog__pager {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
justify-content: space-between;
|
||||||
justify-content: flex-end;
|
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.kingdee-pool-dialog__pager-tip {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -40,9 +40,23 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="uploadUserName" label="上传人" width="140" align="center" />
|
<el-table-column prop="uploadUserName" label="上传人" width="140" align="center" />
|
||||||
<el-table-column prop="uploadTime" label="上传时间" width="180" align="center" sortable />
|
<el-table-column prop="uploadTime" label="上传时间" width="180" align="center" sortable />
|
||||||
<el-table-column v-if="!readonly" label="操作" width="100" align="center" fixed="right">
|
<el-table-column
|
||||||
<template #default="{ $index }">
|
v-if="!readonly || showViewActions"
|
||||||
<el-link type="danger" @click="removeAttachment($index)">删除</el-link>
|
label="操作"
|
||||||
|
:width="readonly ? 120 : 170"
|
||||||
|
align="center"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<div class="vehicle-attachment-table__actions">
|
||||||
|
<el-link type="primary" :disabled="kingdeeBusy" @click="previewAttachment(row)"
|
||||||
|
>查看</el-link
|
||||||
|
>
|
||||||
|
<el-link type="primary" :disabled="kingdeeBusy" @click="downloadAttachment(row)"
|
||||||
|
>下载</el-link
|
||||||
|
>
|
||||||
|
<el-link v-if="!readonly" type="danger" @click="removeAttachment($index)">删除</el-link>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -68,6 +82,7 @@
|
|||||||
width="90%"
|
width="90%"
|
||||||
top="4vh"
|
top="4vh"
|
||||||
class="vehicle-attachment-table__viewer-dialog"
|
class="vehicle-attachment-table__viewer-dialog"
|
||||||
|
@closed="handlePreviewClosed"
|
||||||
>
|
>
|
||||||
<pdf-preview
|
<pdf-preview
|
||||||
v-if="documentPreviewVisible && previewFile.url && previewFile.isPdf"
|
v-if="documentPreviewVisible && previewFile.url && previewFile.isPdf"
|
||||||
@@ -108,6 +123,11 @@ import '@open-file-viewer/core/style.css';
|
|||||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||||
import { downloadFileByUrl } from '@/utils/util';
|
import { downloadFileByUrl } from '@/utils/util';
|
||||||
|
import {
|
||||||
|
downloadKingdeeInvoiceFile,
|
||||||
|
isKingdeePathAttachment,
|
||||||
|
requestKingdeeFile,
|
||||||
|
} from '@/utils/kingdee-attachment';
|
||||||
|
|
||||||
const attachmentFileTypes = [
|
const attachmentFileTypes = [
|
||||||
'pdf',
|
'pdf',
|
||||||
@@ -153,6 +173,16 @@ export default {
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 500,
|
default: 500,
|
||||||
},
|
},
|
||||||
|
// 金蝶侧附件(piaozone path 型)代理下载所依赖的发票号;行内自带 invoiceNo 时优先用行内值
|
||||||
|
kingdeeInvoiceNo: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
// 查看态也展示「查看/下载」操作列(编辑态始终展示,含删除)
|
||||||
|
showViewActions: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
data() {
|
data() {
|
||||||
@@ -164,6 +194,10 @@ export default {
|
|||||||
imagePreviewIndex: 0,
|
imagePreviewIndex: 0,
|
||||||
documentPreviewVisible: false,
|
documentPreviewVisible: false,
|
||||||
previewFile: {},
|
previewFile: {},
|
||||||
|
// 金蝶侧附件代理请求进行中(行级按钮共享 loading 守卫,防重复触发)
|
||||||
|
kingdeeBusy: false,
|
||||||
|
// 金蝶附件预览用的 objectURL,弹窗关闭时回收
|
||||||
|
kingdeeObjectUrl: '',
|
||||||
viewerPlugins,
|
viewerPlugins,
|
||||||
viewerToolbar: {
|
viewerToolbar: {
|
||||||
download: true,
|
download: true,
|
||||||
@@ -198,6 +232,12 @@ export default {
|
|||||||
attachmentUrl(row = {}) {
|
attachmentUrl(row = {}) {
|
||||||
return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || row.src || '';
|
return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || row.src || '';
|
||||||
},
|
},
|
||||||
|
kingdeeInvoiceNoOf(row = {}) {
|
||||||
|
return row.invoiceNo || this.kingdeeInvoiceNo || '';
|
||||||
|
},
|
||||||
|
isKingdeeRow(row = {}) {
|
||||||
|
return isKingdeePathAttachment(row) && Boolean(this.kingdeeInvoiceNoOf(row));
|
||||||
|
},
|
||||||
attachmentExtension(row = {}) {
|
attachmentExtension(row = {}) {
|
||||||
const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0];
|
const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0];
|
||||||
const index = source.lastIndexOf('.');
|
const index = source.lastIndexOf('.');
|
||||||
@@ -245,6 +285,10 @@ export default {
|
|||||||
return `${(value / 1024 / 1024).toFixed(1)}MB`;
|
return `${(value / 1024 / 1024).toFixed(1)}MB`;
|
||||||
},
|
},
|
||||||
downloadAttachment(row) {
|
downloadAttachment(row) {
|
||||||
|
// 金蝶侧附件(piaozone path 型)经后端代理 A801-2 下载,OSS 直链维持前端直连下载
|
||||||
|
if (this.isKingdeeRow(row)) {
|
||||||
|
return this.downloadKingdeeAttachment(row);
|
||||||
|
}
|
||||||
const url = this.attachmentUrl(row);
|
const url = this.attachmentUrl(row);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
this.$message.warning('附件地址为空,无法下载');
|
this.$message.warning('附件地址为空,无法下载');
|
||||||
@@ -252,20 +296,47 @@ export default {
|
|||||||
}
|
}
|
||||||
downloadFileByUrl(url, this.attachmentName(row));
|
downloadFileByUrl(url, this.attachmentName(row));
|
||||||
},
|
},
|
||||||
|
async downloadKingdeeAttachment(row) {
|
||||||
|
if (this.kingdeeBusy) return;
|
||||||
|
this.kingdeeBusy = true;
|
||||||
|
try {
|
||||||
|
await downloadKingdeeInvoiceFile(this.kingdeeInvoiceNoOf(row), this.attachmentName(row));
|
||||||
|
} finally {
|
||||||
|
this.kingdeeBusy = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
batchDownload() {
|
batchDownload() {
|
||||||
const rows = this.selectedRows.length ? this.selectedRows : this.rows;
|
const rows = this.selectedRows.length ? this.selectedRows : this.rows;
|
||||||
const downloadableRows = rows.filter(row => this.attachmentUrl(row));
|
const kingdeeRows = rows.filter(row => this.isKingdeeRow(row));
|
||||||
if (!downloadableRows.length) {
|
const urlRows = rows.filter(row => !this.isKingdeeRow(row) && this.attachmentUrl(row));
|
||||||
|
if (!kingdeeRows.length && !urlRows.length) {
|
||||||
this.$message.warning(
|
this.$message.warning(
|
||||||
this.selectedRows.length ? '所选附件暂无可下载地址' : '暂无可下载附件'
|
this.selectedRows.length ? '所选附件暂无可下载地址' : '暂无可下载附件'
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
downloadableRows.forEach((row, index) => {
|
urlRows.forEach((row, index) => {
|
||||||
window.setTimeout(() => this.downloadAttachment(row), index * 500);
|
window.setTimeout(() => this.downloadAttachment(row), index * 500);
|
||||||
});
|
});
|
||||||
|
// 金蝶侧附件走代理接口,串行触发避免并发重复回源
|
||||||
|
this.downloadKingdeeRows(kingdeeRows);
|
||||||
|
},
|
||||||
|
async downloadKingdeeRows(rows) {
|
||||||
|
if (this.kingdeeBusy) return;
|
||||||
|
this.kingdeeBusy = true;
|
||||||
|
try {
|
||||||
|
for (const row of rows) {
|
||||||
|
await downloadKingdeeInvoiceFile(this.kingdeeInvoiceNoOf(row), this.attachmentName(row));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.kingdeeBusy = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
previewAttachment(row) {
|
previewAttachment(row) {
|
||||||
|
// 金蝶侧附件经 A801-2 代理取流后进弹窗预览,OSS 直链维持原逻辑
|
||||||
|
if (this.isKingdeeRow(row)) {
|
||||||
|
return this.previewKingdeeAttachment(row);
|
||||||
|
}
|
||||||
const url = this.attachmentUrl(row);
|
const url = this.attachmentUrl(row);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
this.$message.warning('附件地址为空,无法预览');
|
this.$message.warning('附件地址为空,无法预览');
|
||||||
@@ -288,6 +359,42 @@ export default {
|
|||||||
};
|
};
|
||||||
this.documentPreviewVisible = true;
|
this.documentPreviewVisible = true;
|
||||||
},
|
},
|
||||||
|
async previewKingdeeAttachment(row) {
|
||||||
|
// OFD 版式文件浏览器无法在线渲染,直接提示下载,避免拉流后预览失败
|
||||||
|
if (this.attachmentExtension(row) === 'ofd') {
|
||||||
|
this.$message.warning('OFD 版式文件不支持在线预览,请使用下载获取后查看');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.kingdeeBusy) return;
|
||||||
|
this.kingdeeBusy = true;
|
||||||
|
try {
|
||||||
|
const blob = await requestKingdeeFile(this.kingdeeInvoiceNoOf(row));
|
||||||
|
this.releaseKingdeeObjectUrl();
|
||||||
|
const isPdf = this.isPdf(row) || String(blob.type || '').includes('application/pdf');
|
||||||
|
this.kingdeeObjectUrl = URL.createObjectURL(blob);
|
||||||
|
this.previewFile = {
|
||||||
|
name: this.attachmentName(row),
|
||||||
|
url: this.kingdeeObjectUrl,
|
||||||
|
mimeType: isPdf ? 'application/pdf' : blob.type || '',
|
||||||
|
isPdf,
|
||||||
|
};
|
||||||
|
this.documentPreviewVisible = true;
|
||||||
|
} catch (error) {
|
||||||
|
if (!error?.silent) this.$message.error(error?.message || '附件预览失败');
|
||||||
|
} finally {
|
||||||
|
this.kingdeeBusy = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handlePreviewClosed() {
|
||||||
|
this.releaseKingdeeObjectUrl();
|
||||||
|
this.previewFile = {};
|
||||||
|
},
|
||||||
|
releaseKingdeeObjectUrl() {
|
||||||
|
if (this.kingdeeObjectUrl) {
|
||||||
|
URL.revokeObjectURL(this.kingdeeObjectUrl);
|
||||||
|
this.kingdeeObjectUrl = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
handlePreviewUnsupported() {
|
handlePreviewUnsupported() {
|
||||||
this.$message.warning('当前文件暂不支持在线预览');
|
this.$message.warning('当前文件暂不支持在线预览');
|
||||||
},
|
},
|
||||||
@@ -318,6 +425,17 @@ export default {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
:deep(.el-link) {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__upload {
|
&__upload {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* 票池发票附件为 {source:'piaozone', path, name}:path 是金蝶侧文件 id(非 http 直链),
|
* 票池发票附件为 {source:'piaozone', path, name}:path 是金蝶侧文件 id(非 http 直链),
|
||||||
* 须由后端代理 A801-2 流式回源;OSS 直链附件(url/link/fileUrl)仍由前端直连下载/预览。
|
* 须由后端代理 A801-2 流式回源;OSS 直链附件(url/link/fileUrl)仍由前端直连下载/预览。
|
||||||
* 三处入口共用:票池弹窗、正式结算认领发票行、付款申请发票附件行。
|
* 入口共用:票池弹窗、正式结算认领发票行、付款申请发票附件行、收票登记附件表。
|
||||||
*/
|
*/
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { downloadFile } from '@/utils/util';
|
import { downloadFile } from '@/utils/util';
|
||||||
@@ -22,11 +22,12 @@ export const isKingdeePathAttachment = attachment => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求金蝶文件流:成功返回 Blob;业务失败为 HTTP 200 + R.fail JSON(响应通道未写流时后端即失败),
|
* 请求金蝶文件流:成功返回 Blob;业务失败为 HTTP 200 + R.fail JSON(响应通道未写流时后端即失败),
|
||||||
* 按 Content-Type 识别并抛出带 msg 的错误;网络级失败拦截器已提示,标记 silent 避免重复提示
|
* 按 Content-Type 识别并抛出带 msg 的错误;网络级失败拦截器已提示,标记 silent 避免重复提示。
|
||||||
|
* 供查看(弹窗内预览)/下载两类入口复用
|
||||||
* @param {String} invoiceNo 发票号码
|
* @param {String} invoiceNo 发票号码
|
||||||
* @returns {Promise<Blob>}
|
* @returns {Promise<Blob>}
|
||||||
*/
|
*/
|
||||||
const requestKingdeeFile = async invoiceNo => {
|
export const requestKingdeeFile = async invoiceNo => {
|
||||||
let res;
|
let res;
|
||||||
try {
|
try {
|
||||||
res = await downloadInvoicePoolAttachment(invoiceNo);
|
res = await downloadInvoicePoolAttachment(invoiceNo);
|
||||||
|
|||||||
@@ -377,8 +377,13 @@ export default {
|
|||||||
},
|
},
|
||||||
async handleApprove(row) {
|
async handleApprove(row) {
|
||||||
await this.$confirm('确认审批通过该开票申请?', '提示', { type: 'warning' });
|
await this.$confirm('确认审批通过该开票申请?', '提示', { type: 'warning' });
|
||||||
await api.approve({ id: row.id });
|
const res = await api.approve({ id: row.id });
|
||||||
this.$message.success('审批通过');
|
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||||
|
if (res.data.data === false) {
|
||||||
|
this.$message.warning(res.data.msg);
|
||||||
|
} else {
|
||||||
|
this.$message.success(res.data.msg || '审批通过');
|
||||||
|
}
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
async handleReturn(row) {
|
async handleReturn(row) {
|
||||||
|
|||||||
@@ -118,7 +118,13 @@
|
|||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card title="附件信息">
|
<section-card title="附件信息">
|
||||||
<vehicle-attachment-table v-model="form.attachments" :readonly="readonly" />
|
<!-- 金蝶票池认领的发票附件为 piaozone path 型,需带发票号走 A801-2 代理查看/下载 -->
|
||||||
|
<vehicle-attachment-table
|
||||||
|
v-model="form.attachments"
|
||||||
|
:readonly="readonly"
|
||||||
|
show-view-actions
|
||||||
|
:kingdee-invoice-no="form.invoiceNo"
|
||||||
|
/>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
<section-card title="备注">
|
<section-card title="备注">
|
||||||
|
|||||||
@@ -342,8 +342,13 @@ export default {
|
|||||||
},
|
},
|
||||||
async handleApprove(row) {
|
async handleApprove(row) {
|
||||||
await this.$confirm('确认审批通过该收票登记?', '提示', { type: 'warning' });
|
await this.$confirm('确认审批通过该收票登记?', '提示', { type: 'warning' });
|
||||||
await api.approve({ id: row.id });
|
const res = await api.approve({ id: row.id });
|
||||||
this.$message.success('审批通过');
|
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||||
|
if (res.data.data === false) {
|
||||||
|
this.$message.warning(res.data.msg);
|
||||||
|
} else {
|
||||||
|
this.$message.success(res.data.msg || '审批通过');
|
||||||
|
}
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
async handleReturn(row) {
|
async handleReturn(row) {
|
||||||
|
|||||||
@@ -387,8 +387,13 @@ export default {
|
|||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
async handleApprove(row) {
|
async handleApprove(row) {
|
||||||
await api.approve({ id: row.id });
|
const res = await api.approve({ id: row.id });
|
||||||
this.$message.success('审批通过');
|
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||||
|
if (res.data.data === false) {
|
||||||
|
this.$message.warning(res.data.msg);
|
||||||
|
} else {
|
||||||
|
this.$message.success(res.data.msg || '审批通过');
|
||||||
|
}
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
handleMkAudit(row) {
|
handleMkAudit(row) {
|
||||||
|
|||||||
@@ -417,7 +417,7 @@
|
|||||||
<span v-else>{{ formatMoney(row.matchedAmount) }}</span>
|
<span v-else>{{ formatMoney(row.matchedAmount) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="220" align="center">
|
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<div class="formal-editor__links">
|
<div class="formal-editor__links">
|
||||||
<el-link
|
<el-link
|
||||||
@@ -451,7 +451,7 @@
|
|||||||
<el-table-column prop="invoiceAmount" label="价税合计" min-width="130" align="center">
|
<el-table-column prop="invoiceAmount" label="价税合计" min-width="130" align="center">
|
||||||
<template #default="{ row }">{{ formatMoney(row.invoiceAmount) }}</template>
|
<template #default="{ row }">{{ formatMoney(row.invoiceAmount) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="160" align="center">
|
<el-table-column label="操作" width="160" fixed="right" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="formal-editor__links">
|
<div class="formal-editor__links">
|
||||||
<el-link type="primary" @click="viewIssuedInvoice(row)">查看</el-link>
|
<el-link type="primary" @click="viewIssuedInvoice(row)">查看</el-link>
|
||||||
|
|||||||
@@ -387,8 +387,13 @@ export default {
|
|||||||
'提示',
|
'提示',
|
||||||
{ type: 'warning' }
|
{ type: 'warning' }
|
||||||
);
|
);
|
||||||
await api.approve({ id: row.id });
|
const res = await api.approve({ id: row.id });
|
||||||
this.$message.success('审批通过');
|
// data=false 表示审批已通过但自动同步金蝶失败,msg 带具体原因
|
||||||
|
if (res.data.data === false) {
|
||||||
|
this.$message.warning(res.data.msg);
|
||||||
|
} else {
|
||||||
|
this.$message.success(res.data.msg || '审批通过');
|
||||||
|
}
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
},
|
},
|
||||||
async handleReturn(row) {
|
async handleReturn(row) {
|
||||||
|
|||||||
Reference in New Issue
Block a user