fix:付款单详情添加导出PDF和打印功能,付款单列表导出添加应付单信息

dev_1.0.2
jiangpeng 2026-04-24 15:38:14 +08:00
parent 4bdbc1daf7
commit c2bbeb7cdc
4 changed files with 288 additions and 21 deletions

View File

@ -12,6 +12,11 @@
<div class="dialog-body" v-if="detail" ref="paymentDrawer"> <div class="dialog-body" v-if="detail" ref="paymentDrawer">
<div class="section"> <div class="section">
<el-divider content-position="left">采购-付款单</el-divider> <el-divider content-position="left">采购-付款单</el-divider>
<div class="section-actions">
<el-button type="primary" size="mini" :loading="pdfExporting" @click="handleExportPDF">PDF</el-button>
<el-button size="mini" @click="handlePrint"></el-button>
</div>
<div ref="pdfExportArea">
<div class="details-container"> <div class="details-container">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
@ -94,6 +99,8 @@
<el-col :span="24"> <el-col :span="24">
<div class="detail-item">特别说明: {{ detail.remark }}</div> <div class="detail-item">特别说明: {{ detail.remark }}</div>
</el-col> </el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24"> <el-col :span="24">
<div class="detail-item"> <div class="detail-item">
<span style="margin-right: 10px">附件:</span> <span style="margin-right: 10px">附件:</span>
@ -101,7 +108,7 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20" v-if="detail.fileList == null || detail.fileList.length < 10"> <el-row :gutter="20" class="export-exclude" v-if="detail.fileList == null || detail.fileList.length < 10">
<el-col :span="24"> <el-col :span="24">
<div class="detail-item"> <div class="detail-item">
<span style="margin-right: 10px">附件补充:</span> <span style="margin-right: 10px">附件补充:</span>
@ -112,7 +119,6 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</div>
<div class="section"> <div class="section">
<el-divider content-position="left">采购-应付单</el-divider> <el-divider content-position="left">采购-应付单</el-divider>
@ -143,6 +149,8 @@
<el-divider content-position="left">流转意见</el-divider> <el-divider content-position="left">流转意见</el-divider>
<flow-opinion-timeline :logs="approveLogs" /> <flow-opinion-timeline :logs="approveLogs" />
</div> </div>
</div>
</div>
</div> </div>
<edit-form :visible.sync="payableVisible" :data="selectedPayableRow" :z-index="2000" @close="payableVisible = false" /> <edit-form :visible.sync="payableVisible" :data="selectedPayableRow" :z-index="2000" @close="payableVisible = false" />
</el-drawer> </el-drawer>
@ -153,6 +161,7 @@ import FileUpload from "@/components/FileUpload";
import EditForm from "@/views/finance/payable/components/EditForm.vue"; import EditForm from "@/views/finance/payable/components/EditForm.vue";
import FlowOpinionTimeline from "@/views/finance/components/FlowOpinionTimeline.vue"; import FlowOpinionTimeline from "@/views/finance/components/FlowOpinionTimeline.vue";
import { listCompletedFlows } from "@/api/flow"; import { listCompletedFlows } from "@/api/flow";
import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default { export default {
name: "DetailDrawer", name: "DetailDrawer",
@ -180,6 +189,7 @@ export default {
approveLogs: [], approveLogs: [],
approveLogsTimer: null, approveLogsTimer: null,
approveLogsLoading: false, approveLogsLoading: false,
pdfExporting: false,
}; };
}, },
beforeDestroy() { beforeDestroy() {
@ -276,6 +286,114 @@ export default {
this.approveLogsLoading = false; this.approveLogsLoading = false;
}); });
}, },
async handleExportPDF() {
if (!this.$refs.pdfExportArea) {
this.$modal.msgWarning("未找到可导出的内容");
return;
}
this.pdfExporting = true;
let exportWrapper = null;
try {
await this.$nextTick();
const sourceEl = this.$refs.pdfExportArea;
const sourceWidth = Math.ceil(sourceEl.getBoundingClientRect().width) || 1000;
//
exportWrapper = document.createElement("div");
exportWrapper.style.position = "fixed";
exportWrapper.style.left = "-10000px";
exportWrapper.style.top = "0";
exportWrapper.style.width = `${sourceWidth}px`;
exportWrapper.style.background = "#F8F5F0";
exportWrapper.style.overflow = "visible";
exportWrapper.style.zIndex = "-1";
exportWrapper.innerHTML = sourceEl.outerHTML;
document.body.appendChild(exportWrapper);
await new Promise(resolve => setTimeout(resolve, 60));
const exportTarget = exportWrapper.firstElementChild || exportWrapper;
exportTarget.querySelectorAll(".export-exclude").forEach(node => node.remove());
const fileName = `付款单详情-${this.detail && this.detail.paymentBillCode ? this.detail.paymentBillCode : ""}.pdf`;
await exportElementToPDF(exportTarget, fileName);
this.$modal.msgSuccess("PDF导出成功");
} catch (error) {
console.error("PDF导出失败:", error);
this.$modal.msgError("PDF导出失败请稍后重试");
} finally {
if (exportWrapper && exportWrapper.parentNode) {
exportWrapper.parentNode.removeChild(exportWrapper);
}
this.pdfExporting = false;
}
},
handlePrint() {
const exportArea = this.$refs.pdfExportArea;
if (!exportArea) {
this.$modal.msgWarning("未找到可打印的内容");
return;
}
const printWindow = window.open("", "_blank");
if (!printWindow) {
this.$modal.msgError("打印窗口被拦截,请允许弹窗后重试");
return;
}
const styleContent = Array.from(document.querySelectorAll("style, link[rel='stylesheet']"))
.map(node => node.outerHTML)
.join("");
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>付款单详情打印</title>
${styleContent}
<style>
@page { margin: 8mm; size: auto; }
html, body {
margin: 0;
padding: 0;
height: auto;
overflow: visible;
}
.print-root {
width: 100%;
transform: scale(0.97);
transform-origin: top left;
page-break-after: avoid;
break-after: avoid-page;
}
.print-root .detail-item:last-child {
margin-bottom: 0 !important;
}
.export-exclude {
display: none !important;
}
@media print {
* {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}
</style>
</head>
<body>
<div class="print-root">${exportArea.outerHTML}</div>
</body>
</html>
`;
printWindow.document.open();
printWindow.document.write(html);
printWindow.document.close();
printWindow.focus();
printWindow.onload = () => {
printWindow.print();
printWindow.close();
};
}
}, },
}; };
</script> </script>
@ -307,6 +425,12 @@ export default {
margin-bottom: 20px; margin-bottom: 20px;
} }
.section-actions {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
}
.red-text{ .red-text{
color: red; color: red;
} }

View File

@ -1,29 +1,27 @@
package com.ruoyi.sip.controller; package com.ruoyi.sip.controller;
import java.lang.reflect.Array; import com.ruoyi.common.annotation.Log;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.ruoyi.common.config.flow.ProcessConfig; import com.ruoyi.common.config.flow.ProcessConfig;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.sip.domain.OmsFinAttachment; import com.ruoyi.sip.domain.OmsFinAttachment;
import com.ruoyi.sip.domain.OmsPaymentBill;
import com.ruoyi.sip.flowable.service.TodoService; import com.ruoyi.sip.flowable.service.TodoService;
import com.ruoyi.sip.service.IOmsFinAttachmentService; import com.ruoyi.sip.service.IOmsFinAttachmentService;
import com.ruoyi.sip.service.IOmsPaymentBillService;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap; import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.sip.domain.OmsPaymentBill;
import com.ruoyi.sip.service.IOmsPaymentBillService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** /**
* Controller * Controller
* *
@ -113,12 +111,7 @@ public class OmsPaymentBillController extends BaseController
@ResponseBody @ResponseBody
public AjaxResult export(OmsPaymentBill omsPaymentBill) public AjaxResult export(OmsPaymentBill omsPaymentBill)
{ {
List<OmsPaymentBill> list = omsPaymentBillService.selectOmsPaymentBillList(omsPaymentBill); return omsPaymentBillService.exportExcelWithPayableDetails(omsPaymentBill);
ExcelUtil<OmsPaymentBill> util = new ExcelUtil<OmsPaymentBill>(OmsPaymentBill.class);
todoService.fillApproveNode(list,
Arrays.asList(processConfig.getDefinition().getFinancePayment(), processConfig.getDefinition().getFinanceRefund())
, OmsPaymentBill::getPaymentBillCode, (a, b) -> a.setApproveNode(b.get(a.getPaymentBillCode())));
return util.exportExcel(list, "采购付款单数据");
} }
/** /**

View File

@ -66,6 +66,8 @@ public interface IOmsPaymentBillService
*/ */
public int deleteOmsPaymentBillById(Long id); public int deleteOmsPaymentBillById(Long id);
AjaxResult exportExcelWithPayableDetails(OmsPaymentBill omsPaymentBill);
PaymentBillDetailDTO query(Long id); PaymentBillDetailDTO query(Long id);
AjaxResult returnPaymentBill(Long id); AjaxResult returnPaymentBill(Long id);

View File

@ -1,5 +1,6 @@
package com.ruoyi.sip.service.impl; package com.ruoyi.sip.service.impl;
import com.alibaba.excel.EasyExcel;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
@ -17,9 +18,11 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.ApproveStatusEnum; import com.ruoyi.common.enums.ApproveStatusEnum;
import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.ShiroUtils; import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.file.FileUploadUtils; import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.mail.TemplateMailUtil; import com.ruoyi.common.utils.mail.TemplateMailUtil;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.*; import com.ruoyi.sip.domain.*;
import com.ruoyi.sip.domain.dto.PaymentBillDetailDTO; import com.ruoyi.sip.domain.dto.PaymentBillDetailDTO;
import com.ruoyi.sip.domain.dto.PaymentBillPayableDetailDTO; import com.ruoyi.sip.domain.dto.PaymentBillPayableDetailDTO;
@ -29,6 +32,7 @@ import com.ruoyi.sip.flowable.domain.Todo;
import com.ruoyi.sip.flowable.service.TodoCommonTemplate; import com.ruoyi.sip.flowable.service.TodoCommonTemplate;
import com.ruoyi.sip.flowable.service.TodoService; import com.ruoyi.sip.flowable.service.TodoService;
import com.ruoyi.sip.service.*; import com.ruoyi.sip.service.*;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.ManagementService; import org.flowable.engine.ManagementService;
import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -45,6 +49,7 @@ import org.springframework.web.multipart.MultipartFile;
* @author ruoyi * @author ruoyi
* @date 2025-10-22 * @date 2025-10-22
*/ */
@Slf4j
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoCommonTemplate public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoCommonTemplate
@ -108,6 +113,149 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
return omsPaymentBillMapper.selectOmsPaymentBillList(omsPaymentBill); return omsPaymentBillMapper.selectOmsPaymentBillList(omsPaymentBill);
} }
@Override
public AjaxResult exportExcelWithPayableDetails(OmsPaymentBill omsPaymentBill) {
List<OmsPaymentBill> list = omsPaymentBillMapper.selectOmsPaymentBillList(omsPaymentBill);
todoService.fillApproveNode(list,
Arrays.asList(processConfig.getDefinition().getFinancePayment(), processConfig.getDefinition().getFinanceRefund())
, OmsPaymentBill::getPaymentBillCode, (a, b) -> a.setApproveNode(b.get(a.getPaymentBillCode())));
List<List<PaymentBillPayableDetailDTO>> payableDetailsGroup = new ArrayList<>();
int maxPayableCount = 0;
for (OmsPaymentBill bill : list) {
List<PaymentBillPayableDetailDTO> payableDetails = Collections.emptyList();
if (bill.getId() != null) {
PaymentBillDetailDTO detailDTO = this.query(bill.getId());
if (detailDTO != null && detailDTO.getPayableDetails() != null) {
payableDetails = detailDTO.getPayableDetails();
}
}
payableDetailsGroup.add(payableDetails);
maxPayableCount = Math.max(maxPayableCount, payableDetails.size());
}
List<List<String>> header = buildExportHeader(maxPayableCount);
List<List<Object>> data = buildExportData(list, payableDetailsGroup, maxPayableCount);
try {
ExcelUtil<OmsPaymentBill> util = new ExcelUtil<>(OmsPaymentBill.class);
String fileName = util.encodingFilename("采购付款单数据");
String filePath = util.getAbsoluteFile(fileName);
EasyExcel.write(filePath).head(header).sheet("采购付款单数据").doWrite(data);
return AjaxResult.success(fileName);
} catch (Exception e) {
log.error("导出采购付款单失败", e);
return AjaxResult.error("导出采购付款单失败:" + e.getMessage());
}
}
private List<List<String>> buildExportHeader(int maxPayableCount) {
List<List<String>> header = new ArrayList<>();
Collections.addAll(header,
Collections.singletonList("项目编号"),
Collections.singletonList("项目名称"),
Collections.singletonList("付款单编号"),
Collections.singletonList("备注"),
Collections.singletonList("预计付款时间"),
Collections.singletonList("制造商名称"),
Collections.singletonList("合同编号"),
Collections.singletonList("含税总价"),
Collections.singletonList("未税总价"),
Collections.singletonList("税额"),
Collections.singletonList("实际付款时间"),
Collections.singletonList("付款状态"),
Collections.singletonList("审批状态"),
Collections.singletonList("审批节点"),
Collections.singletonList("审批时间"),
Collections.singletonList("支付方式"),
Collections.singletonList("应付单编号"),
Collections.singletonList("账户名称"),
Collections.singletonList("银行账号"),
Collections.singletonList("银行开户行"),
Collections.singletonList("银行行号")
);
for (int i = 1; i <= maxPayableCount; i++) {
header.add(Collections.singletonList("应付单" + i + "-项目编号"));
header.add(Collections.singletonList("应付单" + i + "-项目名称"));
header.add(Collections.singletonList("应付单" + i + "-采购应付单编号"));
header.add(Collections.singletonList("应付单" + i + "-含税总价"));
header.add(Collections.singletonList("应付单" + i + "-本次付款金额"));
header.add(Collections.singletonList("应付单" + i + "-本次付款比例"));
}
return header;
}
private List<List<Object>> buildExportData(List<OmsPaymentBill> list,
List<List<PaymentBillPayableDetailDTO>> payableDetailsGroup,
int maxPayableCount) {
List<List<Object>> data = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
OmsPaymentBill bill = list.get(i);
List<PaymentBillPayableDetailDTO> payableDetails = payableDetailsGroup.get(i);
List<Object> row = new ArrayList<>();
Collections.addAll(row,
bill.getProjectCode(),
bill.getProjectName(),
bill.getPaymentBillCode(),
resolvePaymentBillTypeDesc(bill.getPaymentBillType()),
formatDateTime(bill.getPaymentTime()),
bill.getVendorName(),
bill.getOrderCode(),
bill.getTotalPriceWithTax(),
bill.getTotalPriceWithoutTax(),
bill.getTaxAmount(),
formatDateTime(bill.getActualPaymentTime()),
DictUtils.getDictLabel("payment_status", bill.getPaymentStatus()),
DictUtils.getDictLabel("approve_status", bill.getApproveStatus()),
bill.getApproveNode(),
formatDateTime(bill.getApproveTime()),
DictUtils.getDictLabel("payment_method", bill.getPaymentMethod()),
bill.getPayableBillCode(),
bill.getPayName(),
bill.getPayBankNumber(),
bill.getPayBankOpenAddress(),
bill.getBankNumber()
);
for (int index = 0; index < maxPayableCount; index++) {
if (index < payableDetails.size()) {
PaymentBillPayableDetailDTO detail = payableDetails.get(index);
row.add(detail.getProjectCode());
row.add(detail.getProjectName());
row.add(detail.getPayableBillCode());
row.add(detail.getTotalPriceWithTax());
row.add(detail.getPaymentAmount());
row.add(detail.getPaymentRate());
} else {
row.add("");
row.add("");
row.add("");
row.add("");
row.add("");
row.add("");
}
}
data.add(row);
}
return data;
}
private String formatDateTime(Date date) {
return date == null ? "" : DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", date);
}
private String resolvePaymentBillTypeDesc(String paymentBillType) {
if (OmsPaymentBill.PaymentBillTypeEnum.FROM_PAYABLE.getCode().equals(paymentBillType)) {
return OmsPaymentBill.PaymentBillTypeEnum.FROM_PAYABLE.getDesc();
}
if (OmsPaymentBill.PaymentBillTypeEnum.PRE_PAYMENT.getCode().equals(paymentBillType)) {
return OmsPaymentBill.PaymentBillTypeEnum.PRE_PAYMENT.getDesc();
}
if (OmsPaymentBill.PaymentBillTypeEnum.REFUND.getCode().equals(paymentBillType)) {
return OmsPaymentBill.PaymentBillTypeEnum.REFUND.getDesc();
}
return paymentBillType;
}
/** /**
* *
* *