金蝶票池选票统一查镜像前端接线(新增统一选票弹窗组件,替换收票与结算认领弹窗并删除Mock)

This commit is contained in:
刘泉佳
2026-09-22 21:26:50 +08:00
parent a554ccf38e
commit 10fecb5a54
5 changed files with 423 additions and 417 deletions
@@ -0,0 +1,364 @@
<template>
<el-dialog
v-model="visible"
:title="title"
width="92%"
append-to-body
destroy-on-close
:show-close="!syncing"
:close-on-click-modal="!syncing"
:close-on-press-escape="!syncing"
@open="handleOpen"
>
<el-form :model="query" inline label-width="90px" @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.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.invoiceType"
clearable
filterable
placeholder="不限"
style="width: 230px"
>
<el-option v-for="type in invoiceTypeOptions" :key="type" :label="type" :value="type" />
</el-select>
</el-form-item>
<el-form-item label="认领状态">
<el-select v-model="query.claimed" clearable placeholder="不限" style="width: 130px">
<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: 130px">
<el-option label="未登记" :value="false" />
<el-option label="已登记" :value="true" />
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="resetQuery">重置</el-button>
<el-button type="primary" @click="handleQuery">查询</el-button>
<el-button v-if="syncApi" :loading="syncing" @click="handleSync">立即同步</el-button>
</el-form-item>
</el-form>
<el-table
ref="poolTable"
v-loading="loading"
:data="rows"
row-key="id"
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.id" :value="row.id" @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 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 label="金蝶同步" min-width="100" align="center">
<template #default="{ row }">{{ kingdeeStatusText(row.kingdeeStatus) }}</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__footer-bar">
<div class="kingdee-pool-dialog__fetch">
<el-input
v-model="fetchNo"
placeholder="查不到票输入发票号码从金蝶补查"
clearable
style="width: 260px"
@keyup.enter="handleFetch"
/>
<el-button :loading="fetching" @click="handleFetch">补查</el-button>
</div>
<el-pagination
v-model:current-page="page.current"
v-model:page-size="page.size"
:total="page.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="load"
@size-change="handleSizeChange"
/>
</div>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm">{{ confirmText }}</el-button>
</template>
</el-dialog>
</template>
<script>
import { ElLoading } from 'element-plus';
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 },
fetchApi: { type: Function, required: true },
syncApi: { type: Function, default: null },
},
emits: ['update:modelValue', 'confirm'],
data() {
return {
loading: false,
fetching: false,
syncing: false,
// 与后端 BfiConstant.INVOICE_TYPE_NAMES 中文名对齐(镜像落库即中文名)
invoiceTypeOptions: [
'增值税电子普通发票',
'增值税电子专用发票',
'增值税普通发票',
'增值税专用发票',
'增值税普通发票(卷票)',
'通用机打发票',
'出租车票',
'火车票',
'飞机票',
'其他票',
'机动车销售统一发票',
'二手车销售统一发票',
'定额发票',
'增值税电子普通发票(通行费)',
'汽车票',
'过路(过桥)费发票',
'税收完税证明',
'轮船票',
'海关缴款书',
'通用机打电子发票',
'火车票退票凭证',
'财政电子票据',
'数电发票(普通发票)',
'数电发票(增值税专用发票)',
'数电发票(航空运输电子客票行程单)',
'数电发票(铁路电子客票)',
'海外发票',
'海外收据',
'增值税代扣代缴完税凭证',
'数电发票(通行费发票)',
'数电发票(机动车销售统一发票)',
'数电发票(二手车销售统一发票)',
],
rows: [],
selected: [],
current: null,
fetchNo: '',
page: { current: 1, size: 10, total: 0 },
query: this.emptyQuery(),
};
},
computed: {
visible: {
get() {
return this.modelValue;
},
set(value) {
this.$emit('update:modelValue', value);
},
},
},
methods: {
emptyQuery() {
return {
invoiceNo: '',
invoiceDateRange: [],
salerName: '',
buyerName: '',
invoiceType: '',
claimed: null,
registered: null,
};
},
handleOpen() {
this.query = this.emptyQuery();
this.query.invoiceNo = this.initialInvoiceNo || '';
this.current = null;
this.selected = [];
this.$refs.poolTable?.clearSelection();
this.page.current = 1;
this.load();
},
handleQuery() {
this.page.current = 1;
this.load();
},
handleSizeChange() {
this.page.current = 1;
this.load();
},
resetQuery() {
this.query = this.emptyQuery();
this.handleQuery();
},
unwrap(response) {
const body = response?.data || response || {};
return body?.data || body;
},
async load() {
this.loading = true;
try {
const range = this.query.invoiceDateRange || [];
const data = this.unwrap(
await this.queryApi({
current: this.page.current,
size: this.page.size,
invoiceNo: this.query.invoiceNo || undefined,
invoiceDateStart: range[0] || undefined,
invoiceDateEnd: range[1] || undefined,
salerName: this.query.salerName || undefined,
buyerName: this.query.buyerName || undefined,
invoiceType: this.query.invoiceType || undefined,
claimed: this.query.claimed ?? undefined,
registered: this.query.registered ?? undefined,
})
);
this.rows = data.records || [];
this.page.total = Number(data.total || 0);
} 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;
},
async handleFetch() {
const invoiceNo = String(this.fetchNo || '').trim();
if (!invoiceNo) {
this.$message.warning('请输入发票号码');
return;
}
this.fetching = true;
try {
await this.fetchApi(invoiceNo);
this.$message.success('补查成功,已刷新列表');
this.page.current = 1;
await this.load();
} finally {
this.fetching = false;
}
},
async handleSync() {
this.syncing = true;
// 全量同步为服务端分钟级同步接口(per-request 超时 300s),全程遮罩提示并锁定弹窗防误关
const loading = ElLoading.service({
lock: true,
text: '正在从金蝶全票池同步发票,预计 1~3 分钟,请勿关闭窗口…',
background: 'rgba(0, 0, 0, 0.5)',
});
try {
const summary = this.unwrap(await this.syncApi());
this.$message.success(String(summary) || '同步完成');
this.page.current = 1;
await this.load();
} finally {
loading.close();
this.syncing = false;
}
},
formatMoney(value) {
const number = Number(value);
return Number.isFinite(number) ? number.toFixed(2) : '-';
},
kingdeeStatusText(value) {
const statusMap = {
unsynced: '未同步',
synced: '已同步',
failed: '同步失败',
};
return statusMap[value] || value || '-';
},
formatRate(value) {
const number = Number(value);
return Number.isFinite(number) ? `${number}%` : '-';
},
},
};
</script>
<style lang="scss" scoped>
.kingdee-pool-dialog__footer-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 12px;
gap: 16px;
}
.kingdee-pool-dialog__fetch {
display: flex;
align-items: center;
gap: 8px;
}
</style>