Files
tms-erp-web/src/views/payment/bill-ledger.vue
T
gxwebsoft 6de87ed4e2 style(contract): 优化合同管理页面表单样式与布局
- 调整合同管理备注输入框行数和最大长度
- 细化合同变更页面表单项间距和对齐方式
- 统一弹窗表单项的底部间距样式,改为注释禁用
- 调整 port-terminal 和 region 组件的表单项底部间距
- 修改 vite 配置中 API 请求代理地址
- 优化凭证执行详情搜索表单列宽布局
- 在运单管理页面更新型号字段为非必填,调整变更记录内容格式化显示
- 引入并使用 formatRouteChangeContent 工具函数
- 统一明细按钮显示逻辑,草稿状态也显示详情按钮
- 新增路由变更相关工具函数与样式支持
2026-10-14 12:44:56 +08:00

279 lines
9.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<basic-container class="bill-ledger-page">
<bill-ledger-search
v-if="searchVisible"
:query="query"
:loading="loading"
@search="handleSearch"
@reset="resetSearch"
/>
<div class="bill-ledger-page__expiry-tags">
<el-check-tag
v-for="item in shortcutOptions"
:key="item.value"
:checked="query.expiryShortcut === item.value"
@change="handleShortcutChange(item.value)"
>
{{ item.label }}
</el-check-tag>
</div>
<bill-ledger-table
:rows="rows"
:loading="loading"
:page="page"
v-model:search-visible="searchVisible"
@add="openCreate"
@view="openView"
@edit="openEdit"
@delete="handleDelete"
@refresh="loadData"
@page-change="handlePageChange"
@size-change="handleSizeChange"
/>
<el-dialog
v-model="detailDialog.visible"
title="汇票台账详情"
width="86%"
append-to-body
destroy-on-close
>
<div v-loading="detailDialog.loading" class="bill-ledger-detail">
<section-card title="基本信息">
<el-descriptions :column="3" border>
<el-descriptions-item label="票据编号">{{
detailValue('billNo')
}}</el-descriptions-item>
<el-descriptions-item label="汇票类型">{{
detailValue('billTypeName', 'billType')
}}</el-descriptions-item>
<el-descriptions-item label="出票单位">{{
detailValue('issuerName')
}}</el-descriptions-item>
<el-descriptions-item label="收票单位">{{
detailValue('receiverName')
}}</el-descriptions-item>
<el-descriptions-item label="票面金额">{{
formatMoney(detailDialog.row.faceAmount)
}}</el-descriptions-item>
<el-descriptions-item label="可用余额">{{
formatMoney(detailDialog.row.availableBalance)
}}</el-descriptions-item>
<el-descriptions-item label="出票日期">{{
detailValue('issueDate')
}}</el-descriptions-item>
<el-descriptions-item label="到期日期">{{
detailValue('maturityDate')
}}</el-descriptions-item>
<el-descriptions-item label="出票行">{{
detailValue('issuingBank')
}}</el-descriptions-item>
<el-descriptions-item label="可用部门">{{
detailValue('availableDeptNames')
}}</el-descriptions-item>
<el-descriptions-item label="费用承担方">{{
detailValue('feeBearerName')
}}</el-descriptions-item>
<el-descriptions-item label="银行贴现参考率">
{{ formatRate(detailDialog.row.bankDiscountReferenceRate) }}
</el-descriptions-item>
<el-descriptions-item label="双方确认贴现率">
{{ formatRate(detailDialog.row.confirmedDiscountRate) }}
</el-descriptions-item>
<el-descriptions-item label="预计贴现费用">
{{ formatMoney(estimatedDiscountFee()) }}
</el-descriptions-item>
<el-descriptions-item label="备注" :span="3">
{{ detailValue('remark') }}
</el-descriptions-item>
</el-descriptions>
</section-card>
<section-card title="使用记录">
<el-table :data="detailDialog.usageRecords" border>
<el-table-column type="index" label="序号" width="64" align="center" />
<el-table-column prop="applicationNo" label="申请单号" min-width="180" align="center" />
<el-table-column label="使用金额" min-width="150" align="right">
<template #default="{ row }">{{ formatMoney(row.usedAmount) }}</template>
</el-table-column>
<el-table-column prop="useDeptName" label="使用部门" min-width="180" align="center" />
<el-table-column prop="statusName" label="状态" min-width="120" align="center" />
</el-table>
<el-empty v-if="!detailDialog.usageRecords.length" description="暂无使用记录" />
</section-card>
</div>
</el-dialog>
</basic-container>
</template>
<script>
import * as api from '@/api/payment/billLedger';
import * as billPaymentApi from '@/api/payment/billPayment';
import BillLedgerSearch from './components/bill-ledger-search.vue';
import BillLedgerTable from './components/bill-ledger-table.vue';
const emptyQuery = () => ({
billNo: '',
issueDateRange: [],
issuerName: '',
receiverName: '',
billType: '',
maturityStatus: '',
expiryShortcut: 'all',
});
export default {
name: 'BillLedger',
components: { BillLedgerSearch, BillLedgerTable },
data() {
return {
query: emptyQuery(),
counts: { all: 0, within30: 0, within90: 0, over90: 0 },
searchVisible: true,
rows: [],
loading: false,
page: { current: 1, size: 10, total: 0 },
detailDialog: { visible: false, loading: false, row: {}, usageRecords: [] },
};
},
mounted() {
this.loadData();
},
computed: {
shortcutOptions() {
return [
{ label: `全部(${this.counts.all || 0})`, value: 'all' },
{ label: `30天内到期(${this.counts.within30 || 0})`, value: 'within30' },
{ label: `90天内到期(${this.counts.within90 || 0})`, value: 'within90' },
{ label: `90天以上(${this.counts.over90 || 0})`, value: 'over90' },
];
},
},
methods: {
handleShortcutChange(value) {
this.query.expiryShortcut = value;
this.handleSearch();
},
unwrapData(response) {
const body = response?.data || response || {};
return body?.data || body;
},
requestParams() {
const { issueDateRange, ...params } = this.query;
return {
...params,
issueStartDate: issueDateRange?.[0] || '',
issueEndDate: issueDateRange?.[1] || '',
};
},
async loadData() {
this.loading = true;
try {
const [listResponse, countResponse] = await Promise.all([
api.getList(this.page.current, this.page.size, this.requestParams()),
api.getExpiryCounts(),
]);
const data = this.unwrapData(listResponse);
this.rows = data.records || [];
this.page.total = Number(data.total || 0);
this.counts = { ...this.counts, ...(this.unwrapData(countResponse) || {}) };
} finally {
this.loading = false;
}
},
handleSearch() {
this.page.current = 1;
this.loadData();
},
resetSearch() {
this.query = emptyQuery();
this.handleSearch();
},
openCreate() {
this.$router.push({ path: '/payment/bill-ledger/form', query: { mode: 'add' } });
},
async openView(row) {
this.detailDialog = { visible: true, loading: true, row: { ...row }, usageRecords: [] };
try {
const [detailResponse, paymentResponse] = await Promise.all([
api.getDetail(row.id),
billPaymentApi.getList(1, 1000, { billLedgerId: row.id }),
]);
const detail = this.unwrapData(detailResponse) || {};
const paymentData = this.unwrapData(paymentResponse) || {};
const paymentRows = paymentData.records || (Array.isArray(paymentData) ? paymentData : []);
const sourceRecords = paymentRows.length ? paymentRows : detail.usageRecords || [];
this.detailDialog.row = { ...row, ...detail };
this.detailDialog.usageRecords = sourceRecords.map(item => ({
...item,
applicationNo: item.applicationNo || item.paymentNo || '-',
usedAmount: Number(item.usedAmount || 0),
useDeptName: item.useDeptName || item.deptName || '-',
statusName: item.statusName || item.approvalStatusName || '已审核',
}));
} catch (error) {
this.$message.error('票据详情加载失败,请稍后重试');
} finally {
this.detailDialog.loading = false;
}
},
openEdit(row) {
this.$router.push({
path: '/payment/bill-ledger/form',
query: { mode: 'edit', id: row.id },
});
},
async handleDelete(row) {
await this.$confirm('确认删除该汇票台账?', '提示', { type: 'warning' });
await api.remove(row.id);
this.$message.success('删除成功');
if (this.rows.length === 1 && this.page.current > 1) this.page.current -= 1;
this.loadData();
},
handlePageChange(current) {
this.page.current = current;
this.loadData();
},
handleSizeChange(size) {
this.page.current = 1;
this.page.size = size;
this.loadData();
},
detailValue(...fields) {
const value = fields
.map(field => this.detailDialog.row?.[field])
.find(item => item !== undefined && item !== null && item !== '');
return value === undefined ? '-' : value;
},
formatMoney(value) {
return Number(value || 0).toFixed(2);
},
formatRate(value) {
if (Number(value) === -1) return '';
return value === null || value === undefined || value === '' ? '-' : `${Number(value)}%`;
},
estimatedDiscountFee() {
return (
(Number(this.detailDialog.row.faceAmount || 0) *
Number(this.detailDialog.row.confirmedDiscountRate || 0)) /
100
);
},
},
};
</script>
<style scoped lang="scss">
.bill-ledger-page__expiry-tags {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
margin-top: 16px;
margin-bottom: 6px;
:deep(.el-check-tag) {
margin: 0;
white-space: nowrap;
}
}
</style>