622705325b
- 下一页回传上页nextCursor续扫,新查询/换页容量/重新打开自动重置游标与页缓存 - 上一页直接回显已浏览页缓存,不再发起请求 - 查询表单删除单据使用状态条件(后端固定查未用),新增剩余可用金额展示列
311 lines
11 KiB
Vue
311 lines
11 KiB
Vue
<template>
|
|
<el-dialog v-model="visible" :title="title" width="92%" append-to-body destroy-on-close @open="handleOpen">
|
|
<el-form :model="query" inline label-width="130px" @submit.prevent>
|
|
<el-form-item label="发票号码">
|
|
<el-input v-model="query.invoiceNo" clearable @keyup.enter="handleQuery" />
|
|
</el-form-item>
|
|
<el-form-item label="开票日期">
|
|
<el-date-picker
|
|
v-model="query.invoiceDateRange"
|
|
type="daterange"
|
|
value-format="YYYY-MM-DD"
|
|
format="YYYY-MM-DD"
|
|
start-placeholder="开始日期"
|
|
end-placeholder="结束日期"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="销方(开票方税号)">
|
|
<el-input
|
|
v-model="query.salerTaxNo"
|
|
clearable
|
|
style="width: 180px"
|
|
@keyup.enter="handleQuery"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="购方(收票方税号)">
|
|
<el-input
|
|
v-model="query.buyerTaxNo"
|
|
clearable
|
|
style="width: 180px"
|
|
@keyup.enter="handleQuery"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="发票类型">
|
|
<el-select
|
|
v-model="query.invoiceType"
|
|
clearable
|
|
filterable
|
|
placeholder="不限"
|
|
style="width: 230px"
|
|
>
|
|
<el-option
|
|
v-for="option in invoiceTypeOptions"
|
|
:key="option.value"
|
|
:label="option.label"
|
|
:value="option.value"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button :disabled="loading" @click="resetQuery">重置</el-button>
|
|
<el-button type="primary" :disabled="loading" @click="handleQuery">查询</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-table
|
|
ref="poolTable"
|
|
v-loading="loading"
|
|
:data="rows"
|
|
:row-key="rowKey"
|
|
border
|
|
height="520"
|
|
:highlight-current-row="!multiple"
|
|
@selection-change="selection => (selected = selection)"
|
|
@row-dblclick="handleRowDblclick"
|
|
>
|
|
<el-table-column v-if="multiple" type="selection" width="52" fixed="left" align="center" reserve-selection />
|
|
<el-table-column v-else width="52" align="center" fixed="left">
|
|
<template #default="{ row }">
|
|
<el-radio
|
|
:model-value="current && current.invoiceNo"
|
|
:value="row.invoiceNo"
|
|
@change="current = row"
|
|
>
|
|
<span></span>
|
|
</el-radio>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="invoiceNo" label="发票号码" min-width="180" />
|
|
<el-table-column prop="invoiceDate" label="开票日期" min-width="120" />
|
|
<el-table-column prop="invoiceType" label="发票类型" min-width="140" />
|
|
<el-table-column prop="issuerName" label="开票单位" min-width="180" show-overflow-tooltip />
|
|
<el-table-column prop="receiverName" label="受票单位" min-width="180" show-overflow-tooltip />
|
|
<el-table-column label="金额(含税)" min-width="130" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.invoiceAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="税率" min-width="90" align="center">
|
|
<template #default="{ row }">{{ formatRate(row.taxRate) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="税额" min-width="120" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.taxAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column label="剩余可用金额" min-width="130" align="right">
|
|
<template #default="{ row }">{{ formatMoney(row.availableTaxAmount) }}</template>
|
|
</el-table-column>
|
|
<el-table-column prop="kingdeeBillNo" label="金蝶单据号" min-width="210" show-overflow-tooltip />
|
|
<el-table-column label="认领状态" min-width="100" align="center">
|
|
<template #default="{ row }">
|
|
<el-tag v-if="row.claimed" type="danger" size="small">已认领</el-tag>
|
|
<el-tag v-else type="info" size="small">未认领</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="登记状态" min-width="100" align="center">
|
|
<template #default="{ row }">
|
|
<el-tag v-if="row.registered" type="warning" size="small">已登记</el-tag>
|
|
<el-tag v-else type="info" size="small">未登记</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column v-if="!multiple" label="操作" width="90" fixed="right" align="center">
|
|
<template #default="{ row }">
|
|
<el-link type="primary" @click.stop="confirmRows([row])">选择</el-link>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<div class="kingdee-pool-dialog__pager">
|
|
<span class="kingdee-pool-dialog__pager-info">第 {{ page.current }} 页</span>
|
|
<el-button :disabled="page.current <= 1 || loading" @click="prevPage">上一页</el-button>
|
|
<el-button :disabled="!page.hasMore || loading" @click="nextPage">下一页</el-button>
|
|
<el-select v-model="page.size" style="width: 110px" @change="handleSizeChange">
|
|
<el-option v-for="n in [10, 20, 50, 100]" :key="n" :label="`${n}条/页`" :value="n" />
|
|
</el-select>
|
|
</div>
|
|
<template #footer>
|
|
<el-button @click="visible = false">取消</el-button>
|
|
<el-button type="primary" @click="handleConfirm">{{ confirmText }}</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: 'KingdeeInvoicePoolDialog',
|
|
props: {
|
|
modelValue: { type: Boolean, required: true },
|
|
title: { type: String, default: '查询金蝶票据池' },
|
|
confirmText: { type: String, default: '确定' },
|
|
multiple: { type: Boolean, default: false },
|
|
initialInvoiceNo: { type: String, default: '' },
|
|
queryApi: { type: Function, required: true },
|
|
},
|
|
emits: ['update:modelValue', 'confirm'],
|
|
data() {
|
|
return {
|
|
loading: false,
|
|
// 与后端 BfiConstant.INVOICE_TYPE_NAMES 对齐:value 为发票类型编码,label 为中文名
|
|
// 选票场景只开放数电票(普票/专票)
|
|
invoiceTypeOptions: [
|
|
{ value: '26', label: '数电发票(普通发票)' },
|
|
{ value: '27', label: '数电发票(增值税专用发票)' },
|
|
],
|
|
rows: [],
|
|
selected: [],
|
|
current: null,
|
|
page: { current: 1, size: 10, hasMore: false },
|
|
// 续扫游标(服务端过滤补页后翻页须按游标对齐,防漏行/重行)
|
|
nextCursor: null,
|
|
// 下一页请求待回传的游标:nextPage 时暂存,load 成功或失败后清空
|
|
pendingCursor: null,
|
|
// 已浏览页缓存:上一页直接回显不再请求(0 远程调用)
|
|
pageCache: {},
|
|
query: this.emptyQuery(),
|
|
};
|
|
},
|
|
computed: {
|
|
visible: {
|
|
get() {
|
|
return this.modelValue;
|
|
},
|
|
set(value) {
|
|
this.$emit('update:modelValue', value);
|
|
},
|
|
},
|
|
},
|
|
methods: {
|
|
emptyQuery() {
|
|
return {
|
|
invoiceNo: '',
|
|
// 默认查近一个月,随请求传后端并在页面回显
|
|
invoiceDateRange: [this.$dayjs().subtract(1, 'month').format('YYYY-MM-DD'), this.$dayjs().format('YYYY-MM-DD')],
|
|
salerTaxNo: '',
|
|
buyerTaxNo: '',
|
|
invoiceType: '',
|
|
};
|
|
},
|
|
// 远程实时查询的票未入本地镜像,id 恒空,用发票号码兜底作为行主键
|
|
rowKey(row) {
|
|
return row.invoiceNo || row.id;
|
|
},
|
|
handleOpen() {
|
|
this.query = this.emptyQuery();
|
|
this.query.invoiceNo = this.initialInvoiceNo || '';
|
|
this.current = null;
|
|
this.selected = [];
|
|
this.$refs.poolTable?.clearSelection();
|
|
this.page.current = 1;
|
|
this.resetScanState();
|
|
this.load();
|
|
},
|
|
handleQuery() {
|
|
this.page.current = 1;
|
|
this.resetScanState();
|
|
this.load();
|
|
},
|
|
handleSizeChange() {
|
|
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();
|
|
},
|
|
resetQuery() {
|
|
this.query = this.emptyQuery();
|
|
this.handleQuery();
|
|
},
|
|
// 新查询/换页容量/重新打开时清空游标与已浏览页缓存,重新锚定扫描
|
|
resetScanState() {
|
|
this.nextCursor = null;
|
|
this.pendingCursor = null;
|
|
this.pageCache = {};
|
|
},
|
|
unwrap(response) {
|
|
const body = response?.data || response || {};
|
|
return body?.data || body;
|
|
},
|
|
async load() {
|
|
this.loading = true;
|
|
// 请求前快照页码:prev/next 会先改写 current,远程失败时回滚避免停留在错页
|
|
const lastRequested = this.page.current;
|
|
const cursor = this.pendingCursor;
|
|
this.pendingCursor = null;
|
|
try {
|
|
const range = this.query.invoiceDateRange || [];
|
|
const data = this.unwrap(
|
|
await this.queryApi({
|
|
current: this.page.current,
|
|
size: this.page.size,
|
|
cursor: cursor || undefined,
|
|
invoiceNo: this.query.invoiceNo || undefined,
|
|
invoiceDateStart: range[0] || undefined,
|
|
invoiceDateEnd: range[1] || undefined,
|
|
salerTaxNo: this.query.salerTaxNo || undefined,
|
|
buyerTaxNo: this.query.buyerTaxNo || undefined,
|
|
invoiceType: this.query.invoiceType || undefined,
|
|
})
|
|
);
|
|
this.rows = data.records || [];
|
|
this.page.hasMore = !!data.hasMore;
|
|
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 {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
handleRowDblclick(row) {
|
|
if (!this.multiple) {
|
|
this.confirmRows([row]);
|
|
}
|
|
},
|
|
handleConfirm() {
|
|
const picked = this.multiple ? this.selected : this.current ? [this.current] : [];
|
|
this.confirmRows(picked);
|
|
},
|
|
confirmRows(rows) {
|
|
if (!rows.length) {
|
|
this.$message.warning(this.multiple ? '请至少选择一条发票' : '请选择一张发票');
|
|
return;
|
|
}
|
|
this.$emit('confirm', rows);
|
|
this.visible = false;
|
|
},
|
|
formatMoney(value) {
|
|
const number = Number(value);
|
|
return Number.isFinite(number) ? number.toFixed(2) : '-';
|
|
},
|
|
formatRate(value) {
|
|
const number = Number(value);
|
|
return Number.isFinite(number) ? `${number}%` : '-';
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.kingdee-pool-dialog__pager {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
justify-content: flex-end;
|
|
margin-top: 12px;
|
|
}
|
|
</style>
|