feat: 开票付款申请发票冲销改底部弹窗展示并选结算单带出项目利润率

This commit is contained in:
刘泉佳
2026-11-10 12:19:34 +08:00
parent 29a0cdf9fd
commit 0cd2e3b28f
4 changed files with 162 additions and 16 deletions
+123
View File
@@ -0,0 +1,123 @@
<template>
<teleport to="body">
<transition name="el-fade-in-linear">
<section v-if="modelValue" class="bottom-detail-panel" aria-label="详情面板">
<header class="bottom-detail-panel__header">
<h3>{{ title }}</h3>
<el-tooltip content="关闭" placement="top">
<el-button :icon="Close" text aria-label="关闭详情面板" @click="close" />
</el-tooltip>
</header>
<div class="bottom-detail-panel__body">
<slot />
</div>
<footer class="bottom-detail-panel__footer">
<el-button @click="close">关闭</el-button>
</footer>
</section>
</transition>
</teleport>
</template>
<script>
import { Close } from '@element-plus/icons-vue';
/**
* 底部弹出详情面板(表单页通用)
* 固定于视口底部并避让侧边栏,交互对齐应收/应付明细页点「单据号」弹出的单据详情面板
*/
export default {
name: 'BottomDetailPanel',
props: {
modelValue: { type: Boolean, default: false },
title: { type: String, default: '详情' },
},
emits: ['update:modelValue'],
data() {
return { Close };
},
watch: {
// 表单页多被 keep-alive 缓存,切换菜单时组件不销毁,需监听路由收起 teleport 到 body 的面板
$route() {
this.close();
},
},
methods: {
close() {
this.$emit('update:modelValue', false);
},
},
};
</script>
<style scoped lang="scss">
.bottom-detail-panel {
position: fixed;
z-index: 100;
bottom: 0;
left: $sidebar_width;
width: calc(100% - #{$sidebar_width});
max-height: min(60vh, 560px);
border-top: 1px solid #eff1f7;
background: #fff;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.04);
}
:global(.avue--collapse) .bottom-detail-panel {
left: $sidebar_collapse;
width: calc(100% - #{$sidebar_collapse});
}
:global(.avue-layout--horizontal) .bottom-detail-panel {
left: 0;
width: 100%;
}
.bottom-detail-panel__header {
display: flex;
min-height: 48px;
align-items: center;
justify-content: space-between;
padding: 0 20px;
border-bottom: 1px solid #eff1f7;
h3 {
margin: 0;
padding-left: 12px;
border-left: 4px solid #409eff;
font-size: 16px;
font-weight: 600;
}
}
.bottom-detail-panel__body {
max-height: calc(min(60vh, 560px) - 106px);
padding: 16px 20px;
overflow: auto;
:deep(.el-table) {
--el-table-border-color: #eff1f7;
}
:deep(.el-table__row:nth-child(even) td.el-table__cell) {
background: #fafafa;
}
}
.bottom-detail-panel__footer {
display: flex;
min-height: 57px;
align-items: center;
justify-content: flex-end;
gap: 8px;
padding: 0 20px;
border-top: 1px solid #eff1f7;
}
@media screen and (max-width: 992px) {
.bottom-detail-panel {
left: 0;
width: 100%;
}
}
</style>
@@ -43,7 +43,6 @@
</el-table-column>
</el-table-column>
</el-table>
<div v-if="!loading && !rows.length" class="invoice-write-off-table__empty">暂无冲销明细</div>
</div>
</template>
@@ -134,12 +133,3 @@ export default {
},
};
</script>
<style scoped>
.invoice-write-off-table__empty {
padding: 24px 0;
text-align: center;
color: #909399;
font-size: 13px;
}
</style>
+29 -4
View File
@@ -91,7 +91,7 @@
<el-input
:model-value="profitRatePercent"
:disabled="readonly"
placeholder="选结算单后自动带出,可修改"
placeholder="选结算单带出,可修改"
@input="handleProfitRateInput"
>
<template #append>%</template>
@@ -363,6 +363,11 @@
</section-card>
<section-card title="开票明细">
<template #extra>
<el-button v-if="recordId" type="primary" @click="writeOffPanelVisible = true"
>发票冲销</el-button
>
</template>
<el-table
ref="detailTable"
:data="detailRows"
@@ -412,9 +417,9 @@
</el-table>
</section-card>
<section-card v-if="recordId" title="发票冲销明细">
<bottom-detail-panel v-model="writeOffPanelVisible" title="发票冲销明细">
<invoice-write-off-table biz-type="invoice_apply" :biz-id="recordId" />
</section-card>
</bottom-detail-panel>
<section-card title="附件信息">
<vehicle-attachment-table v-model="form.attachments" :readonly="readonly" />
@@ -519,10 +524,12 @@ import * as api from '@/api/payment/invoiceApplication';
import { getAll as getInvoiceItems } from '@/api/base/invoice-item';
import { getList as getWaybillList } from '@/api/business/waybill-manage';
import { getList as getReceivablePayableDetailList } from '@/api/settlement/receivable-payable-detail';
import { getDetail as getProjectApplyDetail } from '@/api/business/project-apply';
import { getDictionary } from '@/api/system/dictbiz';
import { invoiceApplicationDetailColumns } from '@/option/payment/invoiceApplication';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import InvoiceWriteOffTable from '@/components/invoice-write-off-table/main.vue';
import BottomDetailPanel from '@/components/bottom-detail-panel/main.vue';
import { getMkPublicDetail } from '@/api/mk-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
@@ -579,12 +586,14 @@ export default {
components: {
VehicleAttachmentTable,
InvoiceWriteOffTable,
BottomDetailPanel,
},
data() {
return {
form: emptyForm(),
detailRows: [],
selectedDetailIds: [],
writeOffPanelVisible: false,
receiverInvoiceOptions: [],
contactOptions: [],
departmentEmailOptions: [],
@@ -978,11 +987,27 @@ export default {
allocatedInvoiceAmount: 0,
}));
this.form.projectName = first.projectName;
this.form.projectId = first.projectId;
this.form.deptName = first.deptName;
this.form.issuerName = first.issuerName;
this.form.undertakingDeptName = first.deptName;
this.settlementDialog.visible = false;
await Promise.all([this.loadSettlementDetails(), this.loadReceiverInformation()]);
await Promise.all([
this.loadSettlementDetails(),
this.loadReceiverInformation(),
this.loadProjectProfitRate(),
]);
},
// 项目利润率:选结算单后带出项目立项利润率(百分数转小数存 form),已手填时不覆盖
async loadProjectProfitRate() {
if (!this.form.projectId || Number(this.form.profitRate) > 0) return;
try {
const data = this.unwrapData(await getProjectApplyDetail(this.form.projectId));
const rate = Number(data?.profitRate);
if (Number.isFinite(rate) && rate > 0) this.form.profitRate = rate / 100;
} catch {
// 带出失败不阻断选单,保存时后端仍会兜底取项目立项利润率
}
},
async loadSettlementDetails(resetSelection = true) {
this.detailRows =
+10 -2
View File
@@ -290,6 +290,11 @@
</el-table>
</section-card>
<section-card v-if="showInvoiceSection" title="发票信息">
<template #extra>
<el-button v-if="recordId" type="primary" @click="writeOffPanelVisible = true"
>发票冲销</el-button
>
</template>
<el-table :data="form.invoices" border>
<el-table-column type="index" label="序号" width="70" />
<el-table-column prop="settlementNo" label="结算单号" min-width="120">
@@ -350,9 +355,9 @@
</el-table-column>
</el-table>
</section-card>
<section-card v-if="recordId" title="发票冲销明细">
<bottom-detail-panel v-model="writeOffPanelVisible" title="发票冲销明细">
<invoice-write-off-table biz-type="payment_receipt" :biz-id="recordId" />
</section-card>
</bottom-detail-panel>
<section-card title="付款记录">
<el-table :data="paymentRecords" border
><el-table-column type="index" label="序号" width="70" /><el-table-column
@@ -764,6 +769,7 @@ import {
} from '@/utils/kingdee-attachment';
import PdfPreview from '@/components/pdf-preview/main.vue';
import InvoiceWriteOffTable from '@/components/invoice-write-off-table/main.vue';
import BottomDetailPanel from '@/components/bottom-detail-panel/main.vue';
const attachmentViewerPlugins = [
imagePlugin(),
@@ -841,12 +847,14 @@ export default {
OpenFileViewer,
PdfPreview,
InvoiceWriteOffTable,
BottomDetailPanel,
},
data() {
return {
form: emptyForm(),
amountSyncing: false,
transferPayload: null,
writeOffPanelVisible: false,
// 金蝶侧发票附件查看/下载进行中(行组级 loading 守卫)
kingdeeAttachmentBusy: false,
referenceRows: [],