feat(approve): 实现审批单据PDF导出功能

- 引入通用PDF导出工具函数exportElementToPDF
- 在多个审批组件中添加PDF导出按钮和相关逻辑
- 优化导出时的样式显示,隐藏不必要的交互元素
- 统一处理PDF文件命名规则
- 移除原有的html2canvas和jsPDF直接调用代码
- 添加导出状态loading效果和异常处理提示
dev_1.0.0
chenhao 2025-12-16 16:35:53 +08:00
parent af12674d7b
commit 6613b3612c
9 changed files with 428 additions and 121 deletions

View File

@ -34,12 +34,23 @@
<!-- 详情对话框 --> <!-- 详情对话框 -->
<el-dialog title="收票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="收票单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="收票单详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<invoice-receipt-detail :data="form"></invoice-receipt-detail> <el-button
<template #footer> type="primary"
<span>收票编号: {{ form.receiptBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收票单详情">
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
<template #footer>
<span>收票编号: {{ form.receiptBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
@ -67,6 +78,7 @@ import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/inv
import { listCompletedFlows } from "@/api/flow"; import { listCompletedFlows } from "@/api/flow";
import InvoiceReceiptDetail from "../components/InvoiceReceiptDetail"; import InvoiceReceiptDetail from "../components/InvoiceReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "InvoiceReceiptApproved", name: "InvoiceReceiptApproved",
@ -89,7 +101,8 @@ export default {
detailLoading: false, detailLoading: false,
form: {}, form: {},
approveLogs: [], approveLogs: [],
currentInvoiceReceiptId: null currentInvoiceReceiptId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -137,6 +150,20 @@ export default {
getStatusText(status) { getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收票单-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -146,4 +173,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -48,12 +48,23 @@
<!-- 审批详情主对话框 --> <!-- 审批详情主对话框 -->
<el-dialog title="收票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="收票单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="收票单详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<invoice-receipt-detail :data="form"></invoice-receipt-detail> <el-button
<template #footer> type="primary"
<span>收票编号: {{ form.ticketBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="收票单详情">
<invoice-receipt-detail :data="form"></invoice-receipt-detail>
<template #footer>
<span>收票编号: {{ form.ticketBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
@ -96,6 +107,7 @@ import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invo
import { approveTask, listCompletedFlows } from "@/api/flow"; import { approveTask, listCompletedFlows } from "@/api/flow";
import InvoiceReceiptDetail from "./components/InvoiceReceiptDetail"; import InvoiceReceiptDetail from "./components/InvoiceReceiptDetail";
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "InvoiceReceiptApprove", name: "InvoiceReceiptApprove",
@ -129,7 +141,8 @@ export default {
}, },
processKey: 'fianance_ticket', processKey: 'fianance_ticket',
taskId: null, taskId: null,
currentInvoiceReceiptId: null currentInvoiceReceiptId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -226,6 +239,20 @@ export default {
} }
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `收票单-${this.form.ticketBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -235,4 +262,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -36,12 +36,23 @@
<!-- 详情对话框 --> <!-- 详情对话框 -->
<el-dialog title="红冲发票详情" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="红冲发票详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="红冲发票详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<invoice-red-detail :data="form"></invoice-red-detail> <el-button
<template #footer> type="primary"
<span>收票编号: {{ form.receiptBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="红冲发票详情">
<invoice-red-detail :data="form"></invoice-red-detail>
<template #footer>
<span>收票编号: {{ form.receiptBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
@ -69,6 +80,7 @@ import { listInvoiceReceiptApproved, getInvoiceReceipt } from "@/api/finance/inv
import { listCompletedFlows } from "@/api/flow"; import { listCompletedFlows } from "@/api/flow";
import InvoiceRedDetail from "../components/InvoiceRedDetail"; import InvoiceRedDetail from "../components/InvoiceRedDetail";
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "InvoiceRedApproved", name: "InvoiceRedApproved",
@ -84,14 +96,15 @@ export default {
pageSize: 10, pageSize: 10,
receiptNo: null, receiptNo: null,
vendorName: null, vendorName: null,
processKey: 'finance_ticket_refound', processKey: 'fianance_ticket_red',
projectName: null projectName: null
}, },
detailDialogVisible: false, detailDialogVisible: false,
detailLoading: false, detailLoading: false,
form: {}, form: {},
approveLogs: [], approveLogs: [],
currentInvoiceReceiptId: null currentInvoiceReceiptId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -139,6 +152,20 @@ export default {
getStatusText(status) { getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `红冲发票-${this.form.receiptBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -148,4 +175,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -48,12 +48,23 @@
<!-- 审批详情主对话框 --> <!-- 审批详情主对话框 -->
<el-dialog title="红冲发票审批" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="红冲发票审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="红冲发票详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<invoice-red-detail :data="form"></invoice-red-detail> <el-button
<template #footer> type="primary"
<span>收票编号: {{ form.ticketBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="红冲发票详情">
<invoice-red-detail :data="form"></invoice-red-detail>
<template #footer>
<span>收票编号: {{ form.ticketBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
@ -96,6 +107,7 @@ import { listInvoiceReceiptApprove, getInvoiceReceipt } from "@/api/finance/invo
import { approveTask, listCompletedFlows } from "@/api/flow"; import { approveTask, listCompletedFlows } from "@/api/flow";
import InvoiceRedDetail from "./components/InvoiceRedDetail"; import InvoiceRedDetail from "./components/InvoiceRedDetail";
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "InvoiceRedApprove", name: "InvoiceRedApprove",
@ -111,7 +123,7 @@ export default {
pageSize: 10, pageSize: 10,
receiptNo: null, receiptNo: null,
vendorName: null, vendorName: null,
processKey: 'finance_ticket_refound', processKey: 'fianance_ticket_red',
projectName: null projectName: null
}, },
detailDialogVisible: false, detailDialogVisible: false,
@ -127,9 +139,10 @@ export default {
opinionRules: { opinionRules: {
approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }], approveOpinion: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
}, },
processKey: 'finance_ticket_refound', processKey: 'fianance_ticket_red',
taskId: null, taskId: null,
currentInvoiceReceiptId: null currentInvoiceReceiptId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -226,6 +239,20 @@ export default {
} }
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `红冲发票-${this.form.ticketBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -235,4 +262,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -36,23 +36,34 @@
<!-- 详情对话框 --> <!-- 详情对话框 -->
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="付款单详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<payment-detail :data="form"></payment-detail> <el-button
<template #footer> type="primary"
<span>付款编号: {{ form.paymentBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span>付款编号: {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
<el-timeline> <el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top"> <el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card> <el-card>
<h4>{{ log.approveOpinion }}</h4> <h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p> <p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag size="small">{{ getStatusText(log.approveStatus) }}</el-tag></p> <p><b>审批状态:</b> <el-tag size="small">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card> </el-card>
</el-timeline-item> </el-timeline-item>
</el-timeline> </el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div> <div v-if="!approveLogs || approveLogs.length === 0"></div>
</div> </div>
@ -69,6 +80,7 @@ import { listPaymentApproved, getPayment } from "@/api/finance/payment";
import { listCompletedFlows } from "@/api/flow"; import { listCompletedFlows } from "@/api/flow";
import PaymentDetail from "../components/PaymentDetail"; // Relative path adjustment import PaymentDetail from "../components/PaymentDetail"; // Relative path adjustment
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "PaymentApproved", name: "PaymentApproved",
@ -91,7 +103,8 @@ export default {
detailLoading: false, detailLoading: false,
form: {}, form: {},
approveLogs: [], approveLogs: [],
currentPaymentId: null currentPaymentId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -139,6 +152,20 @@ export default {
getStatusText(status) { getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -148,4 +175,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -48,12 +48,23 @@
<!-- 审批详情主对话框 --> <!-- 审批详情主对话框 -->
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="付款单详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<payment-detail :data="form"></payment-detail> <el-button
<template #footer> type="primary"
<span>付款编号: {{ form.paymentBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span>付款编号: {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
@ -96,6 +107,7 @@ import { listPaymentApprove, getPayment } from "@/api/finance/payment";
import { approveTask, listCompletedFlows } from "@/api/flow"; import { approveTask, listCompletedFlows } from "@/api/flow";
import PaymentDetail from "./components/PaymentDetail"; import PaymentDetail from "./components/PaymentDetail";
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "PaymentApprove", name: "PaymentApprove",
@ -129,7 +141,8 @@ export default {
}, },
processKey: 'finance_payment', processKey: 'finance_payment',
taskId: null, taskId: null,
currentPaymentId: null currentPaymentId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -235,6 +248,20 @@ export default {
// Map status codes to text // Map status codes to text
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -244,4 +271,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -36,12 +36,23 @@
<!-- 详情对话框 --> <!-- 详情对话框 -->
<el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="付款单详情" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="付款单详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<payment-detail :data="form"></payment-detail> <el-button
<template #footer> type="primary"
<span>付款编号: {{ form.paymentBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span>付款编号: {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
@ -69,6 +80,7 @@ import { listPaymentApproved, getPayment } from "@/api/finance/payment";
import { listCompletedFlows } from "@/api/flow"; import { listCompletedFlows } from "@/api/flow";
import PaymentDetail from "../components/PaymentRefundDetail.vue"; // Relative path adjustment import PaymentDetail from "../components/PaymentRefundDetail.vue"; // Relative path adjustment
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "PaymentApproved", name: "PaymentApproved",
@ -91,7 +103,8 @@ export default {
detailLoading: false, detailLoading: false,
form: {}, form: {},
approveLogs: [], approveLogs: [],
currentPaymentId: null currentPaymentId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -139,6 +152,20 @@ export default {
getStatusText(status) { getStatusText(status) {
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -148,4 +175,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -48,23 +48,34 @@
<!-- 审批详情主对话框 --> <!-- 审批详情主对话框 -->
<el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body> <el-dialog title="付款单审批" :visible.sync="detailDialogVisible" width="80%" append-to-body>
<div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;"> <div v-loading="detailLoading" style="max-height: 70vh; overflow-y: auto; padding: 20px;">
<ApproveLayout title="付款单详情"> <div style="display: flex;flex-direction: row-reverse; margin-bottom: 10px;">
<payment-detail :data="form"></payment-detail> <el-button
<template #footer> type="primary"
<span>付款编号: {{ form.paymentBillCode }}</span> size="small"
</template> icon="el-icon-download"
</ApproveLayout> @click="exportPDF"
:loading="pdfExporting"
>导出PDF</el-button>
</div>
<div class="approve-container" :class="{ 'exporting-pdf': pdfExporting }">
<ApproveLayout ref="approveLayout" title="付款单详情">
<payment-detail :data="form"></payment-detail>
<template #footer>
<span>付款编号: {{ form.paymentBillCode }}</span>
</template>
</ApproveLayout>
</div>
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<div class="process-container"> <div class="process-container">
<el-timeline> <el-timeline>
<el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top"> <el-timeline-item v-for="(log, index) in approveLogs" :key="index" :timestamp="log.approveTime" placement="top">
<el-card> <el-card>
<h4>{{ log.approveOpinion }}</h4> <h4>{{ log.approveOpinion }}</h4>
<p><b>操作人:</b> {{ log.approveUserName }} </p> <p><b>操作人:</b> {{ log.approveUserName }} </p>
<p><b>审批状态:</b> <el-tag size="small">{{ getStatusText(log.approveStatus) }}</el-tag></p> <p><b>审批状态:</b> <el-tag size="small">{{ getStatusText(log.approveStatus) }}</el-tag></p>
</el-card> </el-card>
</el-timeline-item> </el-timeline-item>
</el-timeline> </el-timeline>
<div v-if="!approveLogs || approveLogs.length === 0"></div> <div v-if="!approveLogs || approveLogs.length === 0"></div>
</div> </div>
@ -96,6 +107,7 @@ import { listPaymentApprove, getPayment } from "@/api/finance/payment";
import { approveTask, listCompletedFlows } from "@/api/flow"; import { approveTask, listCompletedFlows } from "@/api/flow";
import PaymentDetail from "./components/PaymentRefundDetail.vue"; import PaymentDetail from "./components/PaymentRefundDetail.vue";
import ApproveLayout from "@/views/approve/ApproveLayout"; import ApproveLayout from "@/views/approve/ApproveLayout";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "PaymentApprove", name: "PaymentApprove",
@ -129,7 +141,8 @@ export default {
}, },
processKey: 'finance_refund', processKey: 'finance_refund',
taskId: null, taskId: null,
currentPaymentId: null currentPaymentId: null,
pdfExporting: false
}; };
}, },
created() { created() {
@ -235,6 +248,20 @@ export default {
// Map status codes to text // Map status codes to text
const map = { '1': '提交审批', '2': '驳回', '3': '批准' }; const map = { '1': '提交审批', '2': '驳回', '3': '批准' };
return map[status] || '提交审批'; return map[status] || '提交审批';
},
async exportPDF() {
this.pdfExporting = true;
try {
const element = this.$refs.approveLayout.$el;
const fileName = `付款单-${this.form.paymentBillCode || ''}.pdf`;
await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功');
} catch (error) {
console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试');
} finally {
this.pdfExporting = false;
}
} }
} }
}; };
@ -244,4 +271,21 @@ export default {
.process-container { .process-container {
padding: 10px; padding: 10px;
} }
/* 导出PDF时的特殊样式 */
.approve-container.exporting-pdf ::v-deep .el-button--primary,
.approve-container.exporting-pdf ::v-deep .el-button--text {
display: none;
}
.approve-container.exporting-pdf ::v-deep .el-input__inner,
.approve-container.exporting-pdf ::v-deep .el-textarea__inner {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
resize: none !important;
padding: 0 !important;
}
.approve-container.exporting-pdf ::v-deep .el-input__suffix {
display: none;
}
</style> </style>

View File

@ -135,8 +135,7 @@
import { approveOrder,getOrder } from "@/api/approve/order"; import { approveOrder,getOrder } from "@/api/approve/order";
import ConfigInfo from './ConfigInfo.vue'; import ConfigInfo from './ConfigInfo.vue';
import ApproveLayout from '@/views/approve/ApproveLayout.vue'; import ApproveLayout from '@/views/approve/ApproveLayout.vue';
import html2canvas from 'html2canvas'; import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
import jsPDF from 'jspdf';
import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue'; import OrderInfoDisplay from '@/views/project/order/components/OrderInfoDisplay.vue';
@ -468,65 +467,21 @@ export default {
// PDF // PDF
async exportPDF() { async exportPDF() {
this.pdfExporting = true; this.pdfExporting = true;
const disabledElements = [];
try { try {
// ApproveLayoutDOM // ApproveLayoutDOM
const element = this.$refs.approveLayout.$el; const element = this.$refs.approveLayout.$el;
// disabled 便PDF
element.querySelectorAll('input:disabled, textarea:disabled').forEach(el => {
disabledElements.push(el);
el.disabled = false;
});
// 使html2canvas
const canvas = await html2canvas(element, {
scale: 2, //
useCORS: true, //
logging: false, //
backgroundColor: '#F8F5F0' //
});
// PDF
const imgWidth = 210; // A4mm
const pageHeight = 297; // A4mm
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
// PDF
const pdf = new jsPDF('p', 'mm', 'a4');
let position = 0;
// canvas
const imgData = canvas.toDataURL('image/jpeg');
//
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
//
while (heightLeft > 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
// //
const fileName = `${this.order.projectCode || '订单'}-${this.order.orderCode || ''}-Rev.${this.order.versionCode || '1'}.pdf`; const fileName = `${this.order.projectCode || '订单'}-${this.order.orderCode || ''}-Rev.${this.order.versionCode || '1'}.pdf`;
// PDF // PDF
pdf.save(fileName); await exportElementToPDF(element, fileName);
this.$modal.msgSuccess('PDF导出成功'); this.$modal.msgSuccess('PDF导出成功');
} catch (error) { } catch (error) {
console.error('PDF导出失败:', error); console.error('PDF导出失败:', error);
this.$modal.msgError('PDF导出失败请稍后重试'); this.$modal.msgError('PDF导出失败请稍后重试');
} finally { } finally {
// disabled
disabledElements.forEach(el => {
el.disabled = true;
});
this.pdfExporting = false; this.pdfExporting = false;
} }
} }