diff --git a/oms_web/oms_vue/src/views/finance/payment/components/DetailDrawer.vue b/oms_web/oms_vue/src/views/finance/payment/components/DetailDrawer.vue
index b9bae330..f4a33544 100644
--- a/oms_web/oms_vue/src/views/finance/payment/components/DetailDrawer.vue
+++ b/oms_web/oms_vue/src/views/finance/payment/components/DetailDrawer.vue
@@ -12,6 +12,11 @@
采购-付款单
+
+ 导出PDF
+ 打印
+
+
@@ -94,6 +99,8 @@
特别说明: {{ detail.remark }}
+
+
附件:
@@ -101,7 +108,7 @@
-
+
附件补充:
@@ -112,7 +119,6 @@
-
采购-应付单
@@ -143,6 +149,8 @@
流转意见
+
+
@@ -153,6 +161,7 @@ import FileUpload from "@/components/FileUpload";
import EditForm from "@/views/finance/payable/components/EditForm.vue";
import FlowOpinionTimeline from "@/views/finance/components/FlowOpinionTimeline.vue";
import { listCompletedFlows } from "@/api/flow";
+import { exportElementToPDF } from "@/views/approve/finance/pdfUtils";
export default {
name: "DetailDrawer",
@@ -180,6 +189,7 @@ export default {
approveLogs: [],
approveLogsTimer: null,
approveLogsLoading: false,
+ pdfExporting: false,
};
},
beforeDestroy() {
@@ -276,6 +286,114 @@ export default {
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 = `
+
+
+
+
+ 付款单详情打印
+ ${styleContent}
+
+
+
+ ${exportArea.outerHTML}
+
+
+ `;
+
+ printWindow.document.open();
+ printWindow.document.write(html);
+ printWindow.document.close();
+ printWindow.focus();
+ printWindow.onload = () => {
+ printWindow.print();
+ printWindow.close();
+ };
+ }
},
};
@@ -307,6 +425,12 @@ export default {
margin-bottom: 20px;
}
+.section-actions {
+ display: flex;
+ justify-content: flex-end;
+ margin-bottom: 10px;
+}
+
.red-text{
color: red;
}
diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/OmsPaymentBillController.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/OmsPaymentBillController.java
index 1acec693..6265418a 100644
--- a/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/OmsPaymentBillController.java
+++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/OmsPaymentBillController.java
@@ -1,29 +1,27 @@
package com.ruoyi.sip.controller;
-import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
+import com.ruoyi.common.annotation.Log;
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.OmsPaymentBill;
import com.ruoyi.sip.flowable.service.TodoService;
import com.ruoyi.sip.service.IOmsFinAttachmentService;
+import com.ruoyi.sip.service.IOmsPaymentBillService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
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 java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
/**
* 采购付款单Controller
*
@@ -113,12 +111,7 @@ public class OmsPaymentBillController extends BaseController
@ResponseBody
public AjaxResult export(OmsPaymentBill omsPaymentBill)
{
- List list = omsPaymentBillService.selectOmsPaymentBillList(omsPaymentBill);
- ExcelUtil util = new ExcelUtil(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, "采购付款单数据");
+ return omsPaymentBillService.exportExcelWithPayableDetails(omsPaymentBill);
}
/**
diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IOmsPaymentBillService.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IOmsPaymentBillService.java
index c759e29a..031cf8a0 100644
--- a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IOmsPaymentBillService.java
+++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IOmsPaymentBillService.java
@@ -66,6 +66,8 @@ public interface IOmsPaymentBillService
*/
public int deleteOmsPaymentBillById(Long id);
+ AjaxResult exportExcelWithPayableDetails(OmsPaymentBill omsPaymentBill);
+
PaymentBillDetailDTO query(Long id);
AjaxResult returnPaymentBill(Long id);
diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/OmsPaymentBillServiceImpl.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/OmsPaymentBillServiceImpl.java
index c171dff9..1c01915b 100644
--- a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/OmsPaymentBillServiceImpl.java
+++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/OmsPaymentBillServiceImpl.java
@@ -1,5 +1,6 @@
package com.ruoyi.sip.service.impl;
+import com.alibaba.excel.EasyExcel;
import java.math.BigDecimal;
import java.util.*;
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.enums.ApproveStatusEnum;
import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.mail.TemplateMailUtil;
+import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.sip.domain.*;
import com.ruoyi.sip.domain.dto.PaymentBillDetailDTO;
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.TodoService;
import com.ruoyi.sip.service.*;
+import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.ManagementService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,6 +49,7 @@ import org.springframework.web.multipart.MultipartFile;
* @author ruoyi
* @date 2025-10-22
*/
+@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoCommonTemplate
@@ -108,6 +113,149 @@ public class OmsPaymentBillServiceImpl implements IOmsPaymentBillService , TodoC
return omsPaymentBillMapper.selectOmsPaymentBillList(omsPaymentBill);
}
+ @Override
+ public AjaxResult exportExcelWithPayableDetails(OmsPaymentBill omsPaymentBill) {
+ List 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> payableDetailsGroup = new ArrayList<>();
+ int maxPayableCount = 0;
+ for (OmsPaymentBill bill : list) {
+ List 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> header = buildExportHeader(maxPayableCount);
+ List> data = buildExportData(list, payableDetailsGroup, maxPayableCount);
+
+ try {
+ ExcelUtil 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> buildExportHeader(int maxPayableCount) {
+ List> 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> buildExportData(List list,
+ List> payableDetailsGroup,
+ int maxPayableCount) {
+ List> data = new ArrayList<>();
+ for (int i = 0; i < list.size(); i++) {
+ OmsPaymentBill bill = list.get(i);
+ List payableDetails = payableDetailsGroup.get(i);
+ List