From 3440ecd09d9dfc884fe111a2cdd4daff7aae50ef Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期五, 18 九月 2026 15:21:09 +0800
Subject: [PATCH] feat: 检验模块新增纸质单据导出模板(原材料/出厂/过程检验),过程检验新增技术参数等字段并改造判定提交逻辑

---
 src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java |   64 ++
 src/main/java/com/ruoyi/quality/utils/ProcessInspectExportUtil.java         |  381 +++++++++++++++++
 src/main/java/com/ruoyi/quality/utils/FinalInspectExportUtil.java           |  384 +++++++++++++++++
 src/main/java/com/ruoyi/quality/utils/RawMaterialInspectExportUtil.java     |  372 ++++++++++++++++
 src/main/resources/static/junge-logo.png                                    |    0 
 src/main/java/com/ruoyi/quality/pojo/QualityInspect.java                    |   77 +++
 src/main/resources/mapper/quality/QualityInspectMapper.xml                  |    2 
 7 files changed, 1,274 insertions(+), 6 deletions(-)

diff --git a/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java b/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
index 0ebd889..19a0816 100644
--- a/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
+++ b/src/main/java/com/ruoyi/quality/pojo/QualityInspect.java
@@ -96,6 +96,18 @@
     private String model;
 
     /**
+     * 鏉愯川
+     */
+    @Excel(name = "鏉愯川")
+    private String material;
+
+    /**
+     * 楠屾敹渚濇嵁
+     */
+    @Excel(name = "楠屾敹渚濇嵁")
+    private String acceptanceBasis;
+
+    /**
      * 鍗曚綅
      */
     @Excel(name = "鍗曚綅")
@@ -107,6 +119,71 @@
     @Excel(name = "鎬绘暟閲�")
     private BigDecimal quantity;
 
+    /**
+     * 鎶芥鏁�
+     */
+    @Excel(name = "鎶芥鏁�")
+    private BigDecimal samplingCount;
+
+    /**
+     * 鍥剧焊鍏抽敭閮ㄤ綅灏哄
+     */
+    @Excel(name = "鍥剧焊鍏抽敭閮ㄤ綅灏哄")
+    private String drawingKeyDimensions;
+
+    /**
+     * 瀹炴祴鍏抽敭閮ㄤ綅灏哄
+     */
+    @Excel(name = "瀹炴祴鍏抽敭閮ㄤ綅灏哄")
+    private String measuredKeyDimensions;
+
+    /**
+     * 涓昏鎶�鏈弬鏁�(杩囩▼妫�楠�)
+     */
+    @Excel(name = "涓昏鎶�鏈弬鏁�")
+    private String mainTechnicalParams;
+
+    /**
+     * 瀹為檯鎶�鏈弬鏁�(杩囩▼妫�楠�)
+     */
+    @Excel(name = "瀹為檯鎶�鏈弬鏁�")
+    private String actualTechnicalParams;
+
+    /**
+     * 璇存槑涔�(杩囩▼妫�楠�)
+     */
+    @Excel(name = "璇存槑涔�")
+    private String specificationManual;
+
+    /**
+     * 鍏ュ簱缂栧彿
+     */
+    @Excel(name = "鍏ュ簱缂栧彿")
+    private String warehouseCode;
+
+    /**
+     * 鎵�灞炶澶囧悕绉�
+     */
+    @Excel(name = "鎵�灞炶澶囧悕绉�")
+    private String equipmentName;
+
+    /**
+     * 椤圭洰鍚嶇О
+     */
+    @Excel(name = "椤圭洰鍚嶇О")
+    private String projectName;
+
+    /**
+     * 鍒ゅ畾(鍚堟牸/涓嶅悎鏍�)
+     */
+    @Excel(name = "鍒ゅ畾")
+    private String verdict;
+
+    /**
+     * 寮傚父澶囨敞(鍘熸潗鏂欐楠�)
+     */
+    private String abnormalRemark;
+
     @Excel(name = "鍚堟牸鏁伴噺")
     @TableField("qualified_quantity")
     private BigDecimal qualifiedQuantity;
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
index 160e05b..7aa3355 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -21,6 +21,9 @@
 import com.ruoyi.procurementrecord.service.ProcurementRecordService;
 import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.quality.dto.QualityInspectDto;
+import com.ruoyi.quality.utils.FinalInspectExportUtil;
+import com.ruoyi.quality.utils.ProcessInspectExportUtil;
+import com.ruoyi.quality.utils.RawMaterialInspectExportUtil;
 import com.ruoyi.quality.mapper.QualityInspectMapper;
 import com.ruoyi.quality.mapper.QualityTestStandardMapper;
 import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
@@ -82,12 +85,15 @@
     public int add(QualityInspectDto qualityInspectDto) {
         QualityInspect qualityInspect = new QualityInspect();
         BeanUtils.copyProperties(qualityInspectDto, qualityInspect);
+        qualityInspect.setId(null);//寮哄埗璧版暟鎹簱鑷锛岄伩鍏嶅墠绔浼爄d瀵艰嚧涓婚敭鍐茬獊
         qualityInspect.setInspectState(0);//榛樿鏈彁浜�
         qualityInspectMapper.insert(qualityInspect);
-        for (QualityInspectParam qualityInspectParam : qualityInspectDto.getQualityInspectParams()) {
-            qualityInspectParam.setInspectId(qualityInspect.getId());
+        if (ObjectUtils.isNotEmpty(qualityInspectDto.getQualityInspectParams())) {
+            for (QualityInspectParam qualityInspectParam : qualityInspectDto.getQualityInspectParams()) {
+                qualityInspectParam.setInspectId(qualityInspect.getId());
+            }
+            qualityInspectParamService.saveBatch(qualityInspectDto.getQualityInspectParams());
         }
-        qualityInspectParamService.saveBatch(qualityInspectDto.getQualityInspectParams());
         // 淇濆瓨闄勪欢
         fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.QUALITY_INSPECT, qualityInspect.getId(), qualityInspectDto.getStorageBlobDTOs());
         return 0;
@@ -110,9 +116,36 @@
     public int submit(QualityInspect inspect) {
         QualityInspect qualityInspect = qualityInspectMapper.selectById(inspect.getId());
 
+        if (Integer.valueOf(1).equals(qualityInspect.getInspectState())) {
+            throw new ServiceException("璇ヨ褰曞凡鎻愪氦锛屼笉鑳介噸澶嶆彁浜�");
+        }
+
+        // 鍘熸潗鏂欐楠�/杩囩▼妫�楠�/鍑哄巶妫�楠岋細琛ㄥ崟涓嶅啀濉啓鍚堟牸/涓嶅悎鏍兼暟閲忥紝鏀圭敤銆屽垽瀹氥�嶅瓧娈�
+        boolean isVerdictInspect = qualityInspect.getInspectType() != null
+                && (qualityInspect.getInspectType() == 0 || qualityInspect.getInspectType() == 1 || qualityInspect.getInspectType() == 2);
+
         //鎻愪氦鍓嶅繀椤诲垽鏂槸鍚﹀悎鏍�
         if (ObjectUtils.isNull(qualityInspect.getCheckResult())) {
-            throw new ServiceException("璇峰厛鍒ゆ柇鏄惁鍚堟牸");
+            if (isVerdictInspect) {
+                // 鏀圭敤銆屽垽瀹氥�嶅瓧娈�
+                if (ObjectUtils.isNull(qualityInspect.getVerdict())) {
+                    throw new ServiceException("璇峰厛鍒ゅ畾鏄惁鍚堟牸");
+                }
+            } else {
+                throw new ServiceException("璇峰厛鍒ゆ柇鏄惁鍚堟牸");
+            }
+        }
+
+        // 鐢便�屽垽瀹氥�嶆帹瀵煎悎鏍�/涓嶅悎鏍兼暟閲�
+        if (isVerdictInspect) {
+            BigDecimal total = qualityInspect.getQuantity() == null ? BigDecimal.ZERO : qualityInspect.getQuantity();
+            if ("鍚堟牸".equals(qualityInspect.getVerdict())) {
+                qualityInspect.setQualifiedQuantity(total);
+                qualityInspect.setUnqualifiedQuantity(BigDecimal.ZERO);
+            } else if ("涓嶅悎鏍�".equals(qualityInspect.getVerdict())) {
+                qualityInspect.setQualifiedQuantity(BigDecimal.ZERO);
+                qualityInspect.setUnqualifiedQuantity(total);
+            }
         }
 
         if (ObjectUtils.isNull(qualityInspect.getQualifiedQuantity())) {
@@ -166,7 +199,10 @@
             qualityUnqualified.setProductModelId(qualityInspect.getProductModelId());
             List<QualityInspectParam> inspectParams = qualityInspectParamService.list(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, inspect.getId()));
             String text = inspectParams.stream().map(QualityInspectParam::getParameterItem).collect(Collectors.joining(","));
-            qualityUnqualified.setDefectivePhenomena(text + "杩欎簺鎸囨爣涓瓨鍦ㄤ笉鍚堟牸");//涓嶅悎鏍肩幇璞�
+            if (text == null || text.isEmpty()) {
+                text = qualityInspect.getAbnormalRemark();
+            }
+            qualityUnqualified.setDefectivePhenomena((text == null || text.isEmpty()) ? "妫�楠屼笉鍚堟牸" : text + "杩欎簺鎸囨爣涓瓨鍦ㄤ笉鍚堟牸");//涓嶅悎鏍肩幇璞�
             qualityUnqualified.setInspectId(qualityInspect.getId());
             qualityUnqualifiedMapper.insert(qualityUnqualified);
 
@@ -208,6 +244,9 @@
             return R.ok("妫�楠屽崟宸叉彁浜�");
         }
 
+        if (ObjectUtils.isNull(qualityInspect.getVerdict())) {
+            qualityInspect.setVerdict("鍚堟牸");
+        }
         if (ObjectUtils.isNull(qualityInspect.getCheckResult())) {
             qualityInspect.setCheckResult("鍚堟牸");
         }
@@ -336,6 +375,21 @@
     @Override
     public void qualityInspectExport(HttpServletResponse response, QualityInspect qualityInspect) {
         List<QualityInspect> qualityInspects = qualityInspectMapper.qualityInspectExport(qualityInspect);
+        // 鍘熸潗鏂欐楠岋細鎸夌焊璐ㄥ崟鎹牱寮忓鍑�
+        if (qualityInspect.getInspectType() != null && qualityInspect.getInspectType() == 0) {
+            RawMaterialInspectExportUtil.export(response, qualityInspects);
+            return;
+        }
+        // 杩囩▼妫�楠岋細鎸夌焊璐ㄥ崟鎹牱寮忓鍑�
+        if (qualityInspect.getInspectType() != null && qualityInspect.getInspectType() == 1) {
+            ProcessInspectExportUtil.export(response, qualityInspects);
+            return;
+        }
+        // 鍑哄巶妫�楠岋細鎸夌焊璐ㄥ崟鎹牱寮忓鍑�
+        if (qualityInspect.getInspectType() != null && qualityInspect.getInspectType() == 2) {
+            FinalInspectExportUtil.export(response, qualityInspects);
+            return;
+        }
         ExcelUtil<QualityInspect> util = new ExcelUtil<QualityInspect>(QualityInspect.class);
         switch (qualityInspect.getInspectType()) {
             case 0:
diff --git a/src/main/java/com/ruoyi/quality/utils/FinalInspectExportUtil.java b/src/main/java/com/ruoyi/quality/utils/FinalInspectExportUtil.java
new file mode 100644
index 0000000..ed38be3
--- /dev/null
+++ b/src/main/java/com/ruoyi/quality/utils/FinalInspectExportUtil.java
@@ -0,0 +1,384 @@
+package com.ruoyi.quality.utils;
+
+import com.ruoyi.quality.pojo.QualityInspect;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.RegionUtil;
+import org.apache.poi.util.Units;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+/**
+ * 鍑哄巶妫�楠屽鍑猴紙鎸夌焊璐ㄥ崟鎹牱寮忕敓鎴� Excel锛�
+ */
+public class FinalInspectExportUtil {
+
+    // ==================== 鍙畾鍒跺尯鍩燂紙鏀硅繖閲屽嵆鍙皟鏁存ā鏉匡級 ====================
+    /** LOGO 鍥剧墖涓庡崟鍏冩牸杈规涔嬮棿鐨勭暀鐧斤紙鍍忕礌锛� */
+    private static final float LOGO_PADDING_PX = 6f;
+    /** 宸︿笂瑙� LOGO 鍥剧墖锛坈lasspath 璺緞锛屾斁鍦� resources/static 涓嬶級 */
+    private static final String LOGO_IMAGE_PATH = "/static/junge-logo.png";
+    /** 鏈湴寮�鍙戠幆澧� LOGO 缁濆璺緞锛堜紭鍏堣鍙栵級 */
+    private static final String LOGO_ABS_PATH = "D:/develop/XingRuan/product-inventory-management-junge/product-inventory-management/multiple/assets/logo/JunGeJiTuan-logo.png";
+    /** 灞呬腑澶ф爣棰� */
+    private static final String TITLE = "鍑哄巶妫�楠岃褰曡〃";
+    /** 鍥哄畾鏁版嵁琛屾暟锛堟暟鎹笉瓒宠ˉ绌鸿锛岃秴杩囪嚜鍔ㄨ拷鍔狅級 */
+    private static final int FIXED_ROW_COUNT = 14;
+    /** 琛ㄥご鍒楋紙浠庡乏鍒板彸锛� */
+    private static final String[] HEADERS = {
+            "搴忓彿", "鍚嶇О", "鍥惧彿", "鏉愯川", "妫�楠屼緷鎹�", "鏁伴噺", "鎶芥鏁�",
+            "鍥剧焊鍏抽敭閮ㄤ綅灏哄", "瀹炴祴鍏抽敭閮ㄤ綅灏哄", "妫�楠屾棩鏈�", "鍏ュ簱缂栧彿", "鎵�灞炶澶囧悕绉�", "椤圭洰鍚嶅瓧", "鍒ゅ畾"
+    };
+    /** 鍒楀鑷�傚簲锛氭瘡鍒楀湪鍐呭瀹藉害鍩虹涓婇澶栧姞鐨勫搴︼紙绾︾瓑浜庝竴涓眽瀛楀搴︼級 */
+    private static final int COL_WIDTH_PADDING = 2;
+    /** 鍗曞垪鏈�澶у搴︼紙绾︾瓑浜庢眽瀛椾釜鏁帮級锛岄槻姝釜鍒秴闀垮唴瀹规妸鍒楁拺寰楄繃瀹� */
+    private static final int COL_WIDTH_MAX = 40;
+    /** 姝f枃/鏍囬瀛楀彿 */
+    private static final float BODY_FONT_SIZE = 12f;
+    private static final float TITLE_FONT_SIZE = 16f;
+    /** 鏄惁妯悜鎵撳嵃 */
+    private static final boolean LANDSCAPE = true;
+    // ========================================================================
+
+    public static void export(HttpServletResponse response, List<QualityInspect> list) {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            Sheet sheet = workbook.createSheet(TITLE);
+            int colCount = HEADERS.length;
+
+            // 鏍峰紡
+            CellStyle titleStyle = createStyle(workbook, true, TITLE_FONT_SIZE, HorizontalAlignment.CENTER);
+            CellStyle labelStyle = createStyle(workbook, false, BODY_FONT_SIZE, HorizontalAlignment.LEFT);
+            CellStyle headerStyle = createStyle(workbook, true, BODY_FONT_SIZE, HorizontalAlignment.CENTER);
+            CellStyle centerStyle = createStyle(workbook, false, BODY_FONT_SIZE, HorizontalAlignment.CENTER);
+            CellStyle leftStyle = createStyle(workbook, false, BODY_FONT_SIZE, HorizontalAlignment.LEFT);
+            leftStyle.setWrapText(true);
+            // 鏁版嵁鍖烘牱寮忓姞杈规
+            addThinBorder(headerStyle);
+            addThinBorder(centerStyle);
+            addThinBorder(leftStyle);
+
+            // ---- 绗�1~2琛岋細椤电湁 ----
+            Row row1 = sheet.createRow(0);
+            Row row2 = sheet.createRow(1);
+            row1.setHeightInPoints(45);
+            row2.setHeightInPoints(20);
+
+            // 宸︿笂锛氬叕鍙� LOGO 鍥剧墖锛圓1:C2 鍚堝苟鍗曞厓鏍硷級
+            CellRangeAddress logoRegion = new CellRangeAddress(0, 1, 0, 2);
+            sheet.addMergedRegion(logoRegion);
+            Cell logoCell = row1.createCell(0);
+            logoCell.setCellStyle(createStyle(workbook, false, 12f, HorizontalAlignment.CENTER));
+            applyRegionBorder(sheet, logoRegion);
+
+            // 灞呬腑澶ф爣棰橈紙D~K锛�
+            Cell titleCell = row1.createCell(3);
+            titleCell.setCellValue(TITLE);
+            titleCell.setCellStyle(titleStyle);
+            CellRangeAddress titleRegion = new CellRangeAddress(0, 1, 3, colCount - 4);
+            sheet.addMergedRegion(titleRegion);
+            applyRegionBorder(sheet, titleRegion);
+
+            // 鍙充笂锛氭枃浠剁紪鍙� / 椤垫暟锛堥潬宸︼級
+            Cell fileNoCell = row1.createCell(colCount - 3);
+            fileNoCell.setCellValue("鏂囦欢缂栧彿锛�");
+            fileNoCell.setCellStyle(labelStyle);
+            CellRangeAddress fileNoRegion = new CellRangeAddress(0, 0, colCount - 3, colCount - 1);
+            sheet.addMergedRegion(fileNoRegion);
+            applyRegionBorder(sheet, fileNoRegion);
+
+            Cell pageCell = row2.createCell(colCount - 3);
+            pageCell.setCellValue("椤垫暟 / No.锛�");
+            pageCell.setCellStyle(labelStyle);
+            CellRangeAddress pageRegion = new CellRangeAddress(1, 1, colCount - 3, colCount - 1);
+            sheet.addMergedRegion(pageRegion);
+            applyRegionBorder(sheet, pageRegion);
+
+            // ---- 绗�3琛岋細琛ㄥご ----
+            int headerRowIndex = 2;
+            Row headerRow = sheet.createRow(headerRowIndex);
+            headerRow.setHeightInPoints(24);
+            for (int i = 0; i < colCount; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(HEADERS[i]);
+                cell.setCellStyle(headerStyle);
+            }
+
+            // ---- 鏁版嵁琛岋細鍥哄畾琛屾暟锛屼笉瓒宠ˉ绌鸿 ----
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            int dataRows = Math.max(FIXED_ROW_COUNT, list.size());
+            for (int r = 0; r < dataRows; r++) {
+                Row dataRow = sheet.createRow(headerRowIndex + 1 + r);
+                for (int c = 0; c < colCount; c++) {
+                    Cell cell = dataRow.createCell(c);
+                    // 鏂囨湰绫诲垪宸﹀榻愶紝鍏朵綑灞呬腑锛堝簭鍙�/鏁伴噺/鎶芥鏁�/妫�楠屾棩鏈�/鍒ゅ畾灞呬腑锛�
+                    boolean isTextColumn = c == 1 || c == 2 || c == 3 || c == 4 || c == 7 || c == 8
+                            || c == 10 || c == 11 || c == 12;
+                    cell.setCellStyle(isTextColumn ? leftStyle : centerStyle);
+                }
+                // 搴忓彿鍥哄畾缂栧彿 1~N
+                dataRow.getCell(0).setCellValue(r + 1);
+                if (r < list.size()) {
+                    QualityInspect item = list.get(r);
+                    setText(dataRow.getCell(1), item.getProductName());                    // 鍚嶇О
+                    setText(dataRow.getCell(2), item.getModel());                          // 鍥惧彿
+                    setText(dataRow.getCell(3), item.getMaterial());                       // 鏉愯川
+                    setText(dataRow.getCell(4), item.getAcceptanceBasis());                // 妫�楠屼緷鎹�
+                    setText(dataRow.getCell(5), plain(item.getQuantity()));                // 鏁伴噺
+                    setText(dataRow.getCell(6), plain(item.getSamplingCount()));           // 鎶芥鏁�
+                    setText(dataRow.getCell(7), item.getDrawingKeyDimensions());           // 鍥剧焊鍏抽敭閮ㄤ綅灏哄
+                    setText(dataRow.getCell(8), item.getMeasuredKeyDimensions());          // 瀹炴祴鍏抽敭閮ㄤ綅灏哄
+                    setText(dataRow.getCell(9), item.getCheckTime() == null ? "" : sdf.format(item.getCheckTime())); // 妫�楠屾棩鏈�
+                    setText(dataRow.getCell(10), item.getWarehouseCode());                 // 鍏ュ簱缂栧彿
+                    setText(dataRow.getCell(11), item.getEquipmentName());                 // 鎵�灞炶澶囧悕绉�
+                    setText(dataRow.getCell(12), item.getProjectName());                   // 椤圭洰鍚嶅瓧
+                    setText(dataRow.getCell(13), item.getVerdict());                       // 鍒ゅ畾
+                }
+                // 鏍规嵁鏂囨湰鍒楁崲琛岃鏁颁及绠楄楂�
+                dataRow.setHeightInPoints(estimateRowHeight(dataRow, colCount));
+            }
+
+            // 鍒楀鎸夊唴瀹硅嚜閫傚簲
+            autoSizeColumns(sheet, colCount, headerRowIndex, dataRows);
+            // 鍒楀纭畾鍚庡啀宓屽叆 LOGO
+            addLogoImage(workbook, sheet);
+
+            // ---- 椤甸潰璁剧疆锛欰4 ----
+            PrintSetup ps = sheet.getPrintSetup();
+            ps.setPaperSize(PrintSetup.A4_PAPERSIZE);
+            ps.setLandscape(LANDSCAPE);
+            sheet.setMargin(Sheet.TopMargin, 0.4);
+            sheet.setMargin(Sheet.BottomMargin, 0.4);
+            sheet.setMargin(Sheet.LeftMargin, 0.3);
+            sheet.setMargin(Sheet.RightMargin, 0.3);
+            sheet.setFitToPage(true);
+            ps.setFitWidth((short) 1);
+            ps.setFitHeight((short) 0);
+
+            // ---- 杈撳嚭 ----
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            String fileName = URLEncoder.encode(TITLE, "UTF-8");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
+            OutputStream os = response.getOutputStream();
+            workbook.write(os);
+            os.flush();
+        } catch (Exception e) {
+            throw new RuntimeException("瀵煎嚭澶辫触", e);
+        }
+    }
+
+    /** 鍦� A1:C2 鍚堝苟鍖哄祵鍏ュ叕鍙� LOGO 鍥剧墖 */
+    private static void addLogoImage(XSSFWorkbook workbook, Sheet sheet) {
+        byte[] bytes = loadLogoBytes();
+        if (bytes == null) {
+            System.err.println("[FinalInspectExport] LOGO 鍥剧墖鏈壘鍒帮紝瀵煎嚭灏嗕笉甯� LOGO锛堣祫婧愯矾寰�: " + LOGO_IMAGE_PATH + "锛�");
+            return;
+        }
+        try {
+            int pictureIdx = workbook.addPicture(bytes, XSSFWorkbook.PICTURE_TYPE_PNG);
+            XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch();
+            // 鍥剧墖瀹氫綅锛欰1:C1 鍖哄煙鍐�
+            float colAPx = sheet.getColumnWidthInPixels(0);
+            float colBPx = sheet.getColumnWidthInPixels(1);
+            float colCPx = sheet.getColumnWidthInPixels(2);
+            float blockWidthPx = colAPx + colBPx + colCPx;
+            float row0Px = sheet.getRow(0).getHeightInPoints() * 96f / 72f;
+            float row1Px = sheet.getRow(1).getHeightInPoints() * 96f / 72f;
+            float cellHPx = row0Px + row1Px;
+            // 璇诲彇鍥剧墖鐪熷疄瀹介珮
+            int imgW = 236;
+            int imgH = 190;
+            if (bytes.length > 24) {
+                imgW = readInt(bytes, 16);
+                imgH = readInt(bytes, 20);
+            }
+            // 鎸夊崟鍏冩牸澶у皬绛夋瘮缂╂斁锛堝洓鍛ㄧ暀鐧斤級锛屾暣浣撳眳涓�
+            float availW = blockWidthPx - LOGO_PADDING_PX * 2;
+            float availH = cellHPx - LOGO_PADDING_PX * 2;
+            float scale = Math.min(availW / imgW, availH / imgH);
+            float dispW = imgW * scale;
+            float dispH = imgH * scale;
+            float absLeft = LOGO_PADDING_PX + (availW - dispW) / 2f;
+            float absTop = LOGO_PADDING_PX + (availH - dispH) / 2f;
+            float absRight = absLeft + dispW;
+            float absBottom = absTop + dispH;
+            // 璁$畻 from/to 閿氱偣锛氭墍鏈夊亸绉婚噺淇濇寔鍦ㄦ墍鍦ㄥ垪/琛岀殑鍚堟硶鑼冨洿鍐�
+            int fromCol;
+            int fromOffPx;
+            if (absLeft <= colAPx) {
+                fromCol = 0;
+                fromOffPx = (int) absLeft;
+            } else if (absLeft <= colAPx + colBPx) {
+                fromCol = 1;
+                fromOffPx = (int) (absLeft - colAPx);
+            } else {
+                fromCol = 2;
+                fromOffPx = (int) (absLeft - colAPx - colBPx);
+            }
+            int toCol;
+            int toOffPx;
+            if (absRight <= colAPx) {
+                toCol = 0;
+                toOffPx = (int) absRight;
+            } else if (absRight <= colAPx + colBPx) {
+                toCol = 1;
+                toOffPx = (int) (absRight - colAPx);
+            } else {
+                toCol = 2;
+                toOffPx = (int) (absRight - colAPx - colBPx);
+            }
+            int fromRow = 0;
+            float fromRowOff = absTop;
+            if (absTop > row0Px) {
+                fromRow = 1;
+                fromRowOff = absTop - row0Px;
+            }
+            int toRow = 0;
+            float toRowOff = absBottom;
+            if (absBottom > row0Px) {
+                toRow = 1;
+                toRowOff = absBottom - row0Px;
+            }
+            XSSFClientAnchor anchor = new XSSFClientAnchor(
+                    fromOffPx * Units.EMU_PER_PIXEL, (int) (fromRowOff * Units.EMU_PER_PIXEL),
+                    toOffPx * Units.EMU_PER_PIXEL, (int) (toRowOff * Units.EMU_PER_PIXEL),
+                    fromCol, fromRow, toCol, toRow);
+            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
+            drawing.createPicture(anchor, pictureIdx);
+        } catch (Exception e) {
+            // LOGO 鍔犺浇澶辫触涓嶅奖鍝嶅鍑猴紝浣嗘墦鍗伴敊璇究浜庢帓鏌�
+            System.err.println("[FinalInspectExport] LOGO 宓屽叆澶辫触: " + e.getMessage());
+        }
+    }
+
+    /** 璇诲彇 LOGO锛氭湰鍦板紑鍙戜紭鍏堣缁濆璺緞锛屽叾娆� classpath锛屾渶鍚庨」鐩浉瀵硅矾寰� */
+    private static byte[] loadLogoBytes() {
+        try (InputStream is = new java.io.FileInputStream(LOGO_ABS_PATH)) {
+            return is.readAllBytes();
+        } catch (Exception ignored) {
+        }
+        try (InputStream is = FinalInspectExportUtil.class.getResourceAsStream(LOGO_IMAGE_PATH)) {
+            if (is != null) {
+                return is.readAllBytes();
+            }
+        } catch (Exception ignored) {
+        }
+        try (InputStream is = new java.io.FileInputStream("src/main/resources" + LOGO_IMAGE_PATH)) {
+            return is.readAllBytes();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /** 璇诲彇 PNG 澶撮儴鐨勫/楂橈紙澶х鏁存暟锛� */
+    private static int readInt(byte[] b, int off) {
+        return ((b[off] & 0xFF) << 24) | ((b[off + 1] & 0xFF) << 16) | ((b[off + 2] & 0xFF) << 8) | (b[off + 3] & 0xFF);
+    }
+
+    private static CellStyle createStyle(XSSFWorkbook workbook, boolean bold, float fontSize,
+                                         HorizontalAlignment align) {
+        CellStyle style = workbook.createCellStyle();
+        Font font = workbook.createFont();
+        font.setFontName("瀹嬩綋");
+        font.setBold(bold);
+        font.setFontHeightInPoints((short) fontSize);
+        style.setFont(font);
+        style.setAlignment(align);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        return style;
+    }
+
+    private static void addThinBorder(CellStyle style) {
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+    }
+
+    private static void applyRegionBorder(Sheet sheet, CellRangeAddress region) {
+        RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+    }
+
+    private static void setText(Cell cell, String value) {
+        cell.setCellValue(value == null ? "" : value);
+    }
+
+    private static String plain(BigDecimal value) {
+        return value == null ? "" : value.toPlainString();
+    }
+
+    /** 鎸夋枃鏈垪鍐呭涓庡垪瀹戒及绠楁崲琛屽悗鐨勮楂橈紙鐐规暟锛� */
+    private static float estimateRowHeight(Row row, int colCount) {
+        int maxLines = 1;
+        for (int c = 1; c < colCount; c++) {
+            boolean isTextColumn = c == 1 || c == 2 || c == 3 || c == 4 || c == 7 || c == 8
+                    || c == 10 || c == 11 || c == 12;
+            if (!isTextColumn) {
+                continue;
+            }
+            Cell cell = row.getCell(c);
+            String value = cell == null ? null : cell.getStringCellValue();
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+            int lines = (int) Math.ceil(displayLen(value) / (COL_WIDTH_MAX * 2d));
+            maxLines = Math.max(maxLines, lines);
+        }
+        return Math.max(22f, maxLines * 15f + 6f);
+    }
+
+    /** 鍒楀鑷�傚簲锛氭寜琛ㄥご涓庢暟鎹唴瀹硅绠楁瘡鍒楀搴� */
+    private static void autoSizeColumns(Sheet sheet, int colCount, int headerRowIndex, int dataRows) {
+        for (int c = 0; c < colCount; c++) {
+            double maxLen = displayLen(HEADERS[c]);
+            for (int r = 0; r < dataRows; r++) {
+                Row row = sheet.getRow(headerRowIndex + 1 + r);
+                Cell cell = row == null ? null : row.getCell(c);
+                if (cell != null && cell.getCellType() == CellType.STRING) {
+                    maxLen = Math.max(maxLen, displayLen(cell.getStringCellValue()));
+                }
+            }
+            double widthChars = Math.min(maxLen + COL_WIDTH_PADDING, COL_WIDTH_MAX + COL_WIDTH_PADDING);
+            // 涓枃鎸変袱涓崐瑙掑瓧绗﹀搴﹁绠�
+            sheet.setColumnWidth(c, (int) (widthChars * 256));
+        }
+    }
+
+    /** 璁$畻鏄剧ず瀹藉害锛氫腑鏂囩瓑鍏ㄨ瀛楃鎸� 2 涓崐瑙掑搴﹁ */
+    private static double displayLen(String s) {
+        if (s == null) {
+            return 0;
+        }
+        double len = 0;
+        for (char ch : s.toCharArray()) {
+            if ((ch >= 0x2E80 && ch <= 0x9FFF) || (ch >= 0x3000 && ch <= 0x303F) || ch >= 0xFF00) {
+                len += 2;
+            } else {
+                len += 1;
+            }
+        }
+        return len;
+    }
+}
diff --git a/src/main/java/com/ruoyi/quality/utils/ProcessInspectExportUtil.java b/src/main/java/com/ruoyi/quality/utils/ProcessInspectExportUtil.java
new file mode 100644
index 0000000..6e58a7d
--- /dev/null
+++ b/src/main/java/com/ruoyi/quality/utils/ProcessInspectExportUtil.java
@@ -0,0 +1,381 @@
+package com.ruoyi.quality.utils;
+
+import com.ruoyi.quality.pojo.QualityInspect;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.RegionUtil;
+import org.apache.poi.util.Units;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+/**
+ * 杩囩▼妫�楠屽鍑猴紙鎸夌焊璐ㄥ崟鎹牱寮忕敓鎴� Excel锛�
+ */
+public class ProcessInspectExportUtil {
+
+    // ==================== 鍙畾鍒跺尯鍩燂紙鏀硅繖閲屽嵆鍙皟鏁存ā鏉匡級 ====================
+    /** LOGO 鍥剧墖涓庡崟鍏冩牸杈规涔嬮棿鐨勭暀鐧斤紙鍍忕礌锛� */
+    private static final float LOGO_PADDING_PX = 6f;
+    /** 宸︿笂瑙� LOGO 鍥剧墖锛坈lasspath 璺緞锛屾斁鍦� resources/static 涓嬶級 */
+    private static final String LOGO_IMAGE_PATH = "/static/junge-logo.png";
+    /** 鏈湴寮�鍙戠幆澧� LOGO 缁濆璺緞锛堜紭鍏堣鍙栵級 */
+    private static final String LOGO_ABS_PATH = "D:/develop/XingRuan/product-inventory-management-junge/product-inventory-management/multiple/assets/logo/JunGeJiTuan-logo.png";
+    /** 灞呬腑澶ф爣棰� */
+    private static final String TITLE = "杩囩▼妫�楠岃褰曡〃";
+    /** 鍥哄畾鏁版嵁琛屾暟锛堟暟鎹笉瓒宠ˉ绌鸿锛岃秴杩囪嚜鍔ㄨ拷鍔狅級 */
+    private static final int FIXED_ROW_COUNT = 14;
+    /** 琛ㄥご鍒楋紙浠庡乏鍒板彸锛� */
+    private static final String[] HEADERS = {
+            "搴忓彿", "鍚嶇О", "瑙勬牸鍨嬪彿", "鏉愯川", "楠屾敹渚濇嵁", "鏁伴噺", "鎶芥鏁�",
+            "涓昏鎶�鏈弬鏁�", "瀹為檯鎶�鏈弬鏁�", "璇存槑涔�", "妫�楠屾棩鏈�", "鍏ュ簱缂栧彿",
+            "鎵�灞炶澶囧悕绉�", "椤圭洰鍚嶇О", "鍒ゅ畾"
+    };
+    /** 鍒楀鑷�傚簲锛氭瘡鍒楀湪鍐呭瀹藉害鍩虹涓婇澶栧姞鐨勫搴︼紙绾︾瓑浜庝竴涓眽瀛楀搴︼級 */
+    private static final int COL_WIDTH_PADDING = 2;
+    /** 鍗曞垪鏈�澶у搴︼紙绾︾瓑浜庢眽瀛椾釜鏁帮級锛岄槻姝釜鍒秴闀垮唴瀹规妸鍒楁拺寰楄繃瀹� */
+    private static final int COL_WIDTH_MAX = 40;
+    /** 姝f枃/鏍囬瀛楀彿 */
+    private static final float BODY_FONT_SIZE = 12f;
+    private static final float TITLE_FONT_SIZE = 16f;
+    /** 鏄惁妯悜鎵撳嵃 */
+    private static final boolean LANDSCAPE = true;
+    // ========================================================================
+
+    public static void export(HttpServletResponse response, List<QualityInspect> list) {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            Sheet sheet = workbook.createSheet(TITLE);
+            int colCount = HEADERS.length;
+
+            // 鏍峰紡
+            CellStyle titleStyle = createStyle(workbook, true, TITLE_FONT_SIZE, HorizontalAlignment.CENTER);
+            CellStyle labelStyle = createStyle(workbook, false, BODY_FONT_SIZE, HorizontalAlignment.LEFT);
+            CellStyle headerStyle = createStyle(workbook, true, BODY_FONT_SIZE, HorizontalAlignment.CENTER);
+            CellStyle centerStyle = createStyle(workbook, false, BODY_FONT_SIZE, HorizontalAlignment.CENTER);
+            centerStyle.setWrapText(true);
+            // 鏁版嵁鍖烘牱寮忓姞杈规
+            addThinBorder(headerStyle);
+            addThinBorder(centerStyle);
+
+            // ---- 绗�1~2琛岋細椤电湁 ----
+            Row row1 = sheet.createRow(0);
+            Row row2 = sheet.createRow(1);
+            row1.setHeightInPoints(36);
+            row2.setHeightInPoints(16);
+
+            // 宸︿笂锛氬叕鍙� LOGO 鍥剧墖锛圓1:C2 鍚堝苟鍗曞厓鏍硷級
+            CellRangeAddress logoRegion = new CellRangeAddress(0, 1, 0, 2);
+            sheet.addMergedRegion(logoRegion);
+            Cell logoCell = row1.createCell(0);
+            logoCell.setCellStyle(createStyle(workbook, false, 12f, HorizontalAlignment.CENTER));
+            applyRegionBorder(sheet, logoRegion);
+
+            // 灞呬腑澶ф爣棰�
+            Cell titleCell = row1.createCell(3);
+            titleCell.setCellValue(TITLE);
+            titleCell.setCellStyle(titleStyle);
+            CellRangeAddress titleRegion = new CellRangeAddress(0, 1, 3, colCount - 4);
+            sheet.addMergedRegion(titleRegion);
+            applyRegionBorder(sheet, titleRegion);
+
+            // 鍙充笂锛氭枃浠剁紪鍙� / 椤垫暟锛堥潬宸︼級
+            Cell fileNoCell = row1.createCell(colCount - 3);
+            fileNoCell.setCellValue("鏂囦欢缂栧彿锛�");
+            fileNoCell.setCellStyle(labelStyle);
+            CellRangeAddress fileNoRegion = new CellRangeAddress(0, 0, colCount - 3, colCount - 1);
+            sheet.addMergedRegion(fileNoRegion);
+            applyRegionBorder(sheet, fileNoRegion);
+
+            Cell pageCell = row2.createCell(colCount - 3);
+            pageCell.setCellValue("椤垫暟 / No.锛�");
+            pageCell.setCellStyle(labelStyle);
+            CellRangeAddress pageRegion = new CellRangeAddress(1, 1, colCount - 3, colCount - 1);
+            sheet.addMergedRegion(pageRegion);
+            applyRegionBorder(sheet, pageRegion);
+
+            // ---- 绗�3琛岋細琛ㄥご ----
+            int headerRowIndex = 2;
+            Row headerRow = sheet.createRow(headerRowIndex);
+            headerRow.setHeightInPoints(24);
+            for (int i = 0; i < colCount; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(HEADERS[i]);
+                cell.setCellStyle(headerStyle);
+            }
+
+            // ---- 鏁版嵁琛岋細鍥哄畾琛屾暟锛屼笉瓒宠ˉ绌鸿锛屾暟鎹叏閮ㄥ眳涓� ----
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            int dataRows = Math.max(FIXED_ROW_COUNT, list.size());
+            for (int r = 0; r < dataRows; r++) {
+                Row dataRow = sheet.createRow(headerRowIndex + 1 + r);
+                for (int c = 0; c < colCount; c++) {
+                    Cell cell = dataRow.createCell(c);
+                    cell.setCellStyle(centerStyle);
+                }
+                // 搴忓彿鍥哄畾缂栧彿 1~N
+                dataRow.getCell(0).setCellValue(r + 1);
+                if (r < list.size()) {
+                    QualityInspect item = list.get(r);
+                    setText(dataRow.getCell(1), item.getProductName());                 // 鍚嶇О
+                    setText(dataRow.getCell(2), item.getModel());                       // 瑙勬牸鍨嬪彿
+                    setText(dataRow.getCell(3), item.getMaterial());                    // 鏉愯川
+                    setText(dataRow.getCell(4), item.getAcceptanceBasis());             // 楠屾敹渚濇嵁
+                    setText(dataRow.getCell(5), plain(item.getQuantity()));             // 鏁伴噺
+                    setText(dataRow.getCell(6), plain(item.getSamplingCount()));        // 鎶芥鏁�
+                    setText(dataRow.getCell(7), item.getMainTechnicalParams());         // 涓昏鎶�鏈弬鏁�
+                    setText(dataRow.getCell(8), item.getActualTechnicalParams());       // 瀹為檯鎶�鏈弬鏁�
+                    setText(dataRow.getCell(9), item.getSpecificationManual());         // 璇存槑涔�
+                    setText(dataRow.getCell(10), item.getCheckTime() == null ? "" : sdf.format(item.getCheckTime())); // 妫�楠屾棩鏈�
+                    setText(dataRow.getCell(11), item.getWarehouseCode());              // 鍏ュ簱缂栧彿
+                    setText(dataRow.getCell(12), item.getEquipmentName());              // 鎵�灞炶澶囧悕绉�
+                    setText(dataRow.getCell(13), item.getProjectName());                // 椤圭洰鍚嶇О
+                    setText(dataRow.getCell(14), item.getVerdict());                    // 鍒ゅ畾
+                }
+                // 鏍规嵁鏂囨湰鍒楁崲琛岃鏁颁及绠楄楂�
+                dataRow.setHeightInPoints(estimateRowHeight(dataRow, colCount));
+            }
+
+            // 鍒楀鎸夊唴瀹硅嚜閫傚簲
+            autoSizeColumns(sheet, colCount, headerRowIndex, dataRows);
+            // 鍒楀纭畾鍚庡啀宓屽叆 LOGO
+            addLogoImage(workbook, sheet);
+
+            // ---- 椤甸潰璁剧疆锛欰4 ----
+            PrintSetup ps = sheet.getPrintSetup();
+            ps.setPaperSize(PrintSetup.A4_PAPERSIZE);
+            ps.setLandscape(LANDSCAPE);
+            sheet.setMargin(Sheet.TopMargin, 0.4);
+            sheet.setMargin(Sheet.BottomMargin, 0.4);
+            sheet.setMargin(Sheet.LeftMargin, 0.3);
+            sheet.setMargin(Sheet.RightMargin, 0.3);
+            sheet.setFitToPage(true);
+            ps.setFitWidth((short) 1);
+            ps.setFitHeight((short) 0);
+
+            // ---- 杈撳嚭 ----
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            String fileName = URLEncoder.encode(TITLE, "UTF-8");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
+            OutputStream os = response.getOutputStream();
+            workbook.write(os);
+            os.flush();
+        } catch (Exception e) {
+            throw new RuntimeException("瀵煎嚭澶辫触", e);
+        }
+    }
+
+    /** 鍦� A1:C2 鍚堝苟鍖哄祵鍏ュ叕鍙� LOGO 鍥剧墖锛屾寜鍗曞厓鏍艰嚜閫傚簲銆佹暣浣撳眳涓� */
+    private static void addLogoImage(XSSFWorkbook workbook, Sheet sheet) {
+        byte[] bytes = loadLogoBytes();
+        if (bytes == null) {
+            System.err.println("[ProcessInspectExport] LOGO 鍥剧墖鏈壘鍒帮紝瀵煎嚭灏嗕笉甯� LOGO锛堣祫婧愯矾寰�: " + LOGO_IMAGE_PATH + "锛�");
+            return;
+        }
+        try {
+            int pictureIdx = workbook.addPicture(bytes, XSSFWorkbook.PICTURE_TYPE_PNG);
+            XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch();
+            // 鍥剧墖瀹氫綅锛欰1:C2 鍖哄煙鍐�
+            float colAPx = sheet.getColumnWidthInPixels(0);
+            float colBPx = sheet.getColumnWidthInPixels(1);
+            float colCPx = sheet.getColumnWidthInPixels(2);
+            float blockWidthPx = colAPx + colBPx + colCPx;
+            float row0Px = sheet.getRow(0).getHeightInPoints() * 96f / 72f;
+            float row1Px = sheet.getRow(1).getHeightInPoints() * 96f / 72f;
+            float cellHPx = row0Px + row1Px;
+            // 璇诲彇鍥剧墖鐪熷疄瀹介珮
+            int imgW = 236;
+            int imgH = 190;
+            if (bytes.length > 24) {
+                imgW = readInt(bytes, 16);
+                imgH = readInt(bytes, 20);
+            }
+            // 鎸夊崟鍏冩牸澶у皬绛夋瘮缂╂斁锛堝洓鍛ㄧ暀鐧斤級锛屾暣浣撳眳涓�
+            float availW = blockWidthPx - LOGO_PADDING_PX * 2;
+            float availH = cellHPx - LOGO_PADDING_PX * 2;
+            float scale = Math.min(availW / imgW, availH / imgH);
+            float dispW = imgW * scale;
+            float dispH = imgH * scale;
+            float absLeft = LOGO_PADDING_PX + (availW - dispW) / 2f;
+            float absTop = LOGO_PADDING_PX + (availH - dispH) / 2f;
+            float absRight = absLeft + dispW;
+            float absBottom = absTop + dispH;
+            // 璁$畻 from/to 閿氱偣锛氭墍鏈夊亸绉婚噺淇濇寔鍦ㄦ墍鍦ㄥ垪/琛岀殑鍚堟硶鑼冨洿鍐�
+            int fromCol;
+            int fromOffPx;
+            if (absLeft <= colAPx) {
+                fromCol = 0;
+                fromOffPx = (int) absLeft;
+            } else if (absLeft <= colAPx + colBPx) {
+                fromCol = 1;
+                fromOffPx = (int) (absLeft - colAPx);
+            } else {
+                fromCol = 2;
+                fromOffPx = (int) (absLeft - colAPx - colBPx);
+            }
+            int toCol;
+            int toOffPx;
+            if (absRight <= colAPx) {
+                toCol = 0;
+                toOffPx = (int) absRight;
+            } else if (absRight <= colAPx + colBPx) {
+                toCol = 1;
+                toOffPx = (int) (absRight - colAPx);
+            } else {
+                toCol = 2;
+                toOffPx = (int) (absRight - colAPx - colBPx);
+            }
+            int fromRow = 0;
+            float fromRowOff = absTop;
+            if (absTop > row0Px) {
+                fromRow = 1;
+                fromRowOff = absTop - row0Px;
+            }
+            int toRow = 0;
+            float toRowOff = absBottom;
+            if (absBottom > row0Px) {
+                toRow = 1;
+                toRowOff = absBottom - row0Px;
+            }
+            XSSFClientAnchor anchor = new XSSFClientAnchor(
+                    fromOffPx * Units.EMU_PER_PIXEL, (int) (fromRowOff * Units.EMU_PER_PIXEL),
+                    toOffPx * Units.EMU_PER_PIXEL, (int) (toRowOff * Units.EMU_PER_PIXEL),
+                    fromCol, fromRow, toCol, toRow);
+            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
+            drawing.createPicture(anchor, pictureIdx);
+        } catch (Exception e) {
+            // LOGO 鍔犺浇澶辫触涓嶅奖鍝嶅鍑猴紝浣嗘墦鍗伴敊璇究浜庢帓鏌�
+            System.err.println("[ProcessInspectExport] LOGO 宓屽叆澶辫触: " + e.getMessage());
+        }
+    }
+
+    /** 璇诲彇 LOGO锛氭湰鍦板紑鍙戜紭鍏堣缁濆璺緞锛屽叾娆� classpath锛屾渶鍚庨」鐩浉瀵硅矾寰� */
+    private static byte[] loadLogoBytes() {
+        try (InputStream is = new java.io.FileInputStream(LOGO_ABS_PATH)) {
+            return is.readAllBytes();
+        } catch (Exception ignored) {
+        }
+        try (InputStream is = ProcessInspectExportUtil.class.getResourceAsStream(LOGO_IMAGE_PATH)) {
+            if (is != null) {
+                return is.readAllBytes();
+            }
+        } catch (Exception ignored) {
+        }
+        try (InputStream is = new java.io.FileInputStream("src/main/resources" + LOGO_IMAGE_PATH)) {
+            return is.readAllBytes();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /** 鎸夋枃鏈垪鍐呭涓庡垪瀹戒及绠楁崲琛屽悗鐨勮楂橈紙鐐规暟锛� */
+    private static float estimateRowHeight(Row row, int colCount) {
+        int maxLines = 1;
+        for (int c = 1; c < colCount; c++) {
+            boolean isTextColumn = c == 1 || c == 2 || c == 3 || c == 4 || c == 7 || c == 8 || c == 9
+                    || c == 11 || c == 12 || c == 13;
+            if (!isTextColumn) {
+                continue;
+            }
+            Cell cell = row.getCell(c);
+            String value = cell == null ? null : cell.getStringCellValue();
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+            int lines = (int) Math.ceil(displayLen(value) / (COL_WIDTH_MAX * 2d));
+            maxLines = Math.max(maxLines, lines);
+        }
+        return Math.max(22f, maxLines * 15f + 6f);
+    }
+
+    /** 鍒楀鑷�傚簲锛氭寜琛ㄥご涓庢暟鎹唴瀹硅绠楁瘡鍒楀搴� */
+    private static void autoSizeColumns(Sheet sheet, int colCount, int headerRowIndex, int dataRows) {
+        for (int c = 0; c < colCount; c++) {
+            double maxLen = displayLen(HEADERS[c]);
+            for (int r = 0; r < dataRows; r++) {
+                Row row = sheet.getRow(headerRowIndex + 1 + r);
+                Cell cell = row == null ? null : row.getCell(c);
+                if (cell != null && cell.getCellType() == CellType.STRING) {
+                    maxLen = Math.max(maxLen, displayLen(cell.getStringCellValue()));
+                }
+            }
+            double widthChars = Math.min(maxLen + COL_WIDTH_PADDING, COL_WIDTH_MAX + COL_WIDTH_PADDING);
+            // 涓枃鎸変袱涓崐瑙掑瓧绗﹀搴﹁绠�
+            sheet.setColumnWidth(c, (int) (widthChars * 256));
+        }
+    }
+
+    /** 璁$畻鏄剧ず瀹藉害锛氫腑鏂囩瓑鍏ㄨ瀛楃鎸� 2 涓崐瑙掑搴﹁ */
+    private static double displayLen(String s) {
+        if (s == null) {
+            return 0;
+        }
+        double len = 0;
+        for (char ch : s.toCharArray()) {
+            if ((ch >= 0x2E80 && ch <= 0x9FFF) || (ch >= 0x3000 && ch <= 0x303F) || ch >= 0xFF00) {
+                len += 2;
+            } else {
+                len += 1;
+            }
+        }
+        return len;
+    }
+
+    /** 璇诲彇 PNG 澶撮儴鐨勫/楂橈紙澶х鏁存暟锛� */
+    private static int readInt(byte[] b, int off) {
+        return ((b[off] & 0xFF) << 24) | ((b[off + 1] & 0xFF) << 16) | ((b[off + 2] & 0xFF) << 8) | (b[off + 3] & 0xFF);
+    }
+
+    private static CellStyle createStyle(XSSFWorkbook workbook, boolean bold, float fontSize,
+                                         HorizontalAlignment align) {
+        CellStyle style = workbook.createCellStyle();
+        Font font = workbook.createFont();
+        font.setFontName("瀹嬩綋");
+        font.setBold(bold);
+        font.setFontHeightInPoints((short) fontSize);
+        style.setFont(font);
+        style.setAlignment(align);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        return style;
+    }
+
+    private static void addThinBorder(CellStyle style) {
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+    }
+
+    private static void applyRegionBorder(Sheet sheet, CellRangeAddress region) {
+        RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+    }
+
+    private static void setText(Cell cell, String value) {
+        cell.setCellValue(value == null ? "" : value);
+    }
+
+    private static String plain(BigDecimal value) {
+        return value == null ? "" : value.toPlainString();
+    }
+}
diff --git a/src/main/java/com/ruoyi/quality/utils/RawMaterialInspectExportUtil.java b/src/main/java/com/ruoyi/quality/utils/RawMaterialInspectExportUtil.java
new file mode 100644
index 0000000..566edbc
--- /dev/null
+++ b/src/main/java/com/ruoyi/quality/utils/RawMaterialInspectExportUtil.java
@@ -0,0 +1,372 @@
+package com.ruoyi.quality.utils;
+
+import com.ruoyi.quality.pojo.QualityInspect;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.RegionUtil;
+import org.apache.poi.util.Units;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+/**
+ * 鍘熸潗鏂欐楠屽鍑猴紙鎸夌焊璐ㄥ崟鎹牱寮忕敓鎴� Excel锛�
+ */
+public class RawMaterialInspectExportUtil {
+
+    // ==================== 鍙畾鍒跺尯鍩燂紙鏀硅繖閲屽嵆鍙皟鏁存ā鏉匡級 ====================
+    /** LOGO 鍥剧墖涓庡崟鍏冩牸杈规涔嬮棿鐨勭暀鐧斤紙鍍忕礌锛� */
+    private static final float LOGO_PADDING_PX = 6f;
+    /** 宸︿笂瑙� LOGO 鍥剧墖锛坈lasspath 璺緞锛屾斁鍦� resources/static 涓嬶級 */
+    private static final String LOGO_IMAGE_PATH = "/static/junge-logo.png";
+    /** 鏈湴寮�鍙戠幆澧� LOGO 缁濆璺緞锛堜紭鍏堣鍙栵級 */
+    private static final String LOGO_ABS_PATH = "D:/develop/XingRuan/product-inventory-management-junge/product-inventory-management/multiple/assets/logo/JunGeJiTuan-logo.png";
+    /** 灞呬腑澶ф爣棰� */
+    private static final String TITLE = "鏉ユ枡浠跺叆搴撴楠岃褰�";
+    /** 鍥哄畾鏁版嵁琛屾暟锛堟暟鎹笉瓒宠ˉ绌鸿锛岃秴杩囪嚜鍔ㄨ拷鍔狅級 */
+    private static final int FIXED_ROW_COUNT = 14;
+    /** 琛ㄥご鍒楋紙浠庡乏鍒板彸锛� */
+    private static final String[] HEADERS = {
+            "搴忓彿", "椤圭洰", "鐗╂枡鍚嶇О", "妫�楠屼緷鎹�", "鏁伴噺", "鎶芥鏁伴噺", "妫�楠屾棩鏈�", "寮傚父澶囨敞", "鍒ゅ畾"
+    };
+    /** 鍒楀鑷�傚簲锛氭瘡鍒楀湪鍐呭瀹藉害鍩虹涓婇澶栧姞鐨勫搴︼紙绾︾瓑浜庝竴涓眽瀛楀搴︼級 */
+    private static final int COL_WIDTH_PADDING = 2;
+    /** 鍗曞垪鏈�澶у搴︼紙绾︾瓑浜庢眽瀛椾釜鏁帮級锛岄槻姝釜鍒秴闀垮唴瀹规妸鍒楁拺寰楄繃瀹� */
+    private static final int COL_WIDTH_MAX = 40;
+    /** 姝f枃/鏍囬瀛楀彿 */
+    private static final float BODY_FONT_SIZE = 12f;
+    private static final float TITLE_FONT_SIZE = 16f;
+    /** 鏄惁妯悜鎵撳嵃 */
+    private static final boolean LANDSCAPE = true;
+    // ========================================================================
+
+    public static void export(HttpServletResponse response, List<QualityInspect> list) {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            Sheet sheet = workbook.createSheet(TITLE);
+            int colCount = HEADERS.length;
+
+            // 鏍峰紡
+            CellStyle titleStyle = createStyle(workbook, true, TITLE_FONT_SIZE, HorizontalAlignment.CENTER);
+            CellStyle labelStyle = createStyle(workbook, false, BODY_FONT_SIZE, HorizontalAlignment.LEFT);
+            CellStyle headerStyle = createStyle(workbook, true, BODY_FONT_SIZE, HorizontalAlignment.CENTER);
+            CellStyle centerStyle = createStyle(workbook, false, BODY_FONT_SIZE, HorizontalAlignment.CENTER);
+            centerStyle.setWrapText(true);
+            // 鏁版嵁鍖烘牱寮忓姞杈规
+            addThinBorder(headerStyle);
+            addThinBorder(centerStyle);
+
+            // ---- 绗�1~2琛岋細椤电湁 ----
+            Row row1 = sheet.createRow(0);
+            Row row2 = sheet.createRow(1);
+            row1.setHeightInPoints(36);
+            row2.setHeightInPoints(16);
+
+            // 宸︿笂锛氬叕鍙� LOGO 鍥剧墖锛圓1:C2 鍚堝苟鍗曞厓鏍硷級
+            CellRangeAddress logoRegion = new CellRangeAddress(0, 1, 0, 2);
+            sheet.addMergedRegion(logoRegion);
+            Cell logoCell = row1.createCell(0);
+            logoCell.setCellStyle(createStyle(workbook, false, 12f, HorizontalAlignment.CENTER));
+            applyRegionBorder(sheet, logoRegion);
+
+            // 灞呬腑澶ф爣棰橈紙D~F锛�
+            Cell titleCell = row1.createCell(3);
+            titleCell.setCellValue(TITLE);
+            titleCell.setCellStyle(titleStyle);
+            CellRangeAddress titleRegion = new CellRangeAddress(0, 1, 3, colCount - 4);
+            sheet.addMergedRegion(titleRegion);
+            applyRegionBorder(sheet, titleRegion);
+
+            // 鍙充笂锛氭枃浠剁紪鍙� / 椤垫暟锛堥潬宸︼級
+            Cell fileNoCell = row1.createCell(colCount - 3);
+            fileNoCell.setCellValue("鏂囦欢缂栧彿锛�");
+            fileNoCell.setCellStyle(labelStyle);
+            CellRangeAddress fileNoRegion = new CellRangeAddress(0, 0, colCount - 3, colCount - 1);
+            sheet.addMergedRegion(fileNoRegion);
+            applyRegionBorder(sheet, fileNoRegion);
+
+            Cell pageCell = row2.createCell(colCount - 3);
+            pageCell.setCellValue("椤垫暟 / No.锛�");
+            pageCell.setCellStyle(labelStyle);
+            CellRangeAddress pageRegion = new CellRangeAddress(1, 1, colCount - 3, colCount - 1);
+            sheet.addMergedRegion(pageRegion);
+            applyRegionBorder(sheet, pageRegion);
+
+            // ---- 绗�3琛岋細琛ㄥご ----
+            int headerRowIndex = 2;
+            Row headerRow = sheet.createRow(headerRowIndex);
+            headerRow.setHeightInPoints(24);
+            for (int i = 0; i < colCount; i++) {
+                Cell cell = headerRow.createCell(i);
+                cell.setCellValue(HEADERS[i]);
+                cell.setCellStyle(headerStyle);
+            }
+
+            // ---- 鏁版嵁琛岋細鍥哄畾琛屾暟锛屼笉瓒宠ˉ绌鸿 ----
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            int dataRows = Math.max(FIXED_ROW_COUNT, list.size());
+            for (int r = 0; r < dataRows; r++) {
+                Row dataRow = sheet.createRow(headerRowIndex + 1 + r);
+                for (int c = 0; c < colCount; c++) {
+                    Cell cell = dataRow.createCell(c);
+                    cell.setCellStyle(centerStyle);
+                }
+                // 搴忓彿鍥哄畾缂栧彿 1~N
+                dataRow.getCell(0).setCellValue(r + 1);
+                if (r < list.size()) {
+                    QualityInspect item = list.get(r);
+                    setText(dataRow.getCell(1), item.getProjectName());                   // 椤圭洰
+                    setText(dataRow.getCell(2), item.getProductName());                   // 鐗╂枡鍚嶇О
+                    setText(dataRow.getCell(3), item.getAcceptanceBasis());               // 妫�楠屼緷鎹�
+                    setText(dataRow.getCell(4), plain(item.getQuantity()));               // 鏁伴噺
+                    setText(dataRow.getCell(5), plain(item.getSamplingCount()));          // 鎶芥鏁伴噺
+                    setText(dataRow.getCell(6), item.getCheckTime() == null ? "" : sdf.format(item.getCheckTime())); // 妫�楠屾棩鏈�
+                    setText(dataRow.getCell(7), item.getAbnormalRemark());                // 寮傚父澶囨敞
+                    setText(dataRow.getCell(8), item.getVerdict());                       // 鍒ゅ畾
+                }
+                // 鏍规嵁鏂囨湰鍒楁崲琛岃鏁颁及绠楄楂�
+                dataRow.setHeightInPoints(estimateRowHeight(dataRow, colCount));
+            }
+
+            // 鍒楀鎸夊唴瀹硅嚜閫傚簲
+            autoSizeColumns(sheet, colCount, headerRowIndex, dataRows);
+            // 鍒楀纭畾鍚庡啀宓屽叆 LOGO
+            addLogoImage(workbook, sheet);
+
+            // ---- 椤甸潰璁剧疆锛欰4 ----
+            PrintSetup ps = sheet.getPrintSetup();
+            ps.setPaperSize(PrintSetup.A4_PAPERSIZE);
+            ps.setLandscape(LANDSCAPE);
+            sheet.setMargin(Sheet.TopMargin, 0.4);
+            sheet.setMargin(Sheet.BottomMargin, 0.4);
+            sheet.setMargin(Sheet.LeftMargin, 0.3);
+            sheet.setMargin(Sheet.RightMargin, 0.3);
+            sheet.setFitToPage(true);
+            ps.setFitWidth((short) 1);
+            ps.setFitHeight((short) 0);
+
+            // ---- 杈撳嚭 ----
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            String fileName = URLEncoder.encode(TITLE, "UTF-8");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
+            OutputStream os = response.getOutputStream();
+            workbook.write(os);
+            os.flush();
+        } catch (Exception e) {
+            throw new RuntimeException("瀵煎嚭澶辫触", e);
+        }
+    }
+
+    /** 鍦� A1:C2 鍚堝苟鍖哄祵鍏ュ叕鍙� LOGO 鍥剧墖锛屾寜鍗曞厓鏍艰嚜閫傚簲銆佹暣浣撳眳涓� */
+    private static void addLogoImage(XSSFWorkbook workbook, Sheet sheet) {
+        byte[] bytes = loadLogoBytes();
+        if (bytes == null) {
+            System.err.println("[RawMaterialInspectExport] LOGO 鍥剧墖鏈壘鍒帮紝瀵煎嚭灏嗕笉甯� LOGO锛堣祫婧愯矾寰�: " + LOGO_IMAGE_PATH + "锛�");
+            return;
+        }
+        try {
+            int pictureIdx = workbook.addPicture(bytes, XSSFWorkbook.PICTURE_TYPE_PNG);
+            XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch();
+            // 鍥剧墖瀹氫綅锛欰1:C2 鍖哄煙鍐�
+            float colAPx = sheet.getColumnWidthInPixels(0);
+            float colBPx = sheet.getColumnWidthInPixels(1);
+            float colCPx = sheet.getColumnWidthInPixels(2);
+            float blockWidthPx = colAPx + colBPx + colCPx;
+            float row0Px = sheet.getRow(0).getHeightInPoints() * 96f / 72f;
+            float row1Px = sheet.getRow(1).getHeightInPoints() * 96f / 72f;
+            float cellHPx = row0Px + row1Px;
+            // 璇诲彇鍥剧墖鐪熷疄瀹介珮
+            int imgW = 236;
+            int imgH = 190;
+            if (bytes.length > 24) {
+                imgW = readInt(bytes, 16);
+                imgH = readInt(bytes, 20);
+            }
+            // 鎸夊崟鍏冩牸澶у皬绛夋瘮缂╂斁锛堝洓鍛ㄧ暀鐧斤級锛屾暣浣撳眳涓�
+            float availW = blockWidthPx - LOGO_PADDING_PX * 2;
+            float availH = cellHPx - LOGO_PADDING_PX * 2;
+            float scale = Math.min(availW / imgW, availH / imgH);
+            float dispW = imgW * scale;
+            float dispH = imgH * scale;
+            float absLeft = LOGO_PADDING_PX + (availW - dispW) / 2f;
+            float absTop = LOGO_PADDING_PX + (availH - dispH) / 2f;
+            float absRight = absLeft + dispW;
+            float absBottom = absTop + dispH;
+            // 璁$畻 from/to 閿氱偣锛氭墍鏈夊亸绉婚噺淇濇寔鍦ㄦ墍鍦ㄥ垪/琛岀殑鍚堟硶鑼冨洿鍐�
+            int fromCol;
+            int fromOffPx;
+            if (absLeft <= colAPx) {
+                fromCol = 0;
+                fromOffPx = (int) absLeft;
+            } else if (absLeft <= colAPx + colBPx) {
+                fromCol = 1;
+                fromOffPx = (int) (absLeft - colAPx);
+            } else {
+                fromCol = 2;
+                fromOffPx = (int) (absLeft - colAPx - colBPx);
+            }
+            int toCol;
+            int toOffPx;
+            if (absRight <= colAPx) {
+                toCol = 0;
+                toOffPx = (int) absRight;
+            } else if (absRight <= colAPx + colBPx) {
+                toCol = 1;
+                toOffPx = (int) (absRight - colAPx);
+            } else {
+                toCol = 2;
+                toOffPx = (int) (absRight - colAPx - colBPx);
+            }
+            int fromRow = 0;
+            float fromRowOff = absTop;
+            if (absTop > row0Px) {
+                fromRow = 1;
+                fromRowOff = absTop - row0Px;
+            }
+            int toRow = 0;
+            float toRowOff = absBottom;
+            if (absBottom > row0Px) {
+                toRow = 1;
+                toRowOff = absBottom - row0Px;
+            }
+            XSSFClientAnchor anchor = new XSSFClientAnchor(
+                    fromOffPx * Units.EMU_PER_PIXEL, (int) (fromRowOff * Units.EMU_PER_PIXEL),
+                    toOffPx * Units.EMU_PER_PIXEL, (int) (toRowOff * Units.EMU_PER_PIXEL),
+                    fromCol, fromRow, toCol, toRow);
+            anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
+            drawing.createPicture(anchor, pictureIdx);
+        } catch (Exception e) {
+            // LOGO 鍔犺浇澶辫触涓嶅奖鍝嶅鍑猴紝浣嗘墦鍗伴敊璇究浜庢帓鏌�
+            System.err.println("[RawMaterialInspectExport] LOGO 宓屽叆澶辫触: " + e.getMessage());
+        }
+    }
+
+    /** 璇诲彇 LOGO锛氭湰鍦板紑鍙戜紭鍏堣缁濆璺緞锛屽叾娆� classpath锛屾渶鍚庨」鐩浉瀵硅矾寰� */
+    private static byte[] loadLogoBytes() {
+        try (InputStream is = new java.io.FileInputStream(LOGO_ABS_PATH)) {
+            return is.readAllBytes();
+        } catch (Exception ignored) {
+        }
+        try (InputStream is = RawMaterialInspectExportUtil.class.getResourceAsStream(LOGO_IMAGE_PATH)) {
+            if (is != null) {
+                return is.readAllBytes();
+            }
+        } catch (Exception ignored) {
+        }
+        try (InputStream is = new java.io.FileInputStream("src/main/resources" + LOGO_IMAGE_PATH)) {
+            return is.readAllBytes();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /** 鎸夋枃鏈垪鍐呭涓庡垪瀹戒及绠楁崲琛屽悗鐨勮楂橈紙鐐规暟锛� */
+    private static float estimateRowHeight(Row row, int colCount) {
+        int maxLines = 1;
+        for (int c = 1; c < colCount; c++) {
+            boolean isTextColumn = c == 1 || c == 2 || c == 3 || c == 7;
+            if (!isTextColumn) {
+                continue;
+            }
+            Cell cell = row.getCell(c);
+            String value = cell == null ? null : cell.getStringCellValue();
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+            int lines = (int) Math.ceil(displayLen(value) / (COL_WIDTH_MAX * 2d));
+            maxLines = Math.max(maxLines, lines);
+        }
+        return Math.max(22f, maxLines * 15f + 6f);
+    }
+
+    /** 鍒楀鑷�傚簲锛氭寜琛ㄥご涓庢暟鎹唴瀹硅绠楁瘡鍒楀搴� */
+    private static void autoSizeColumns(Sheet sheet, int colCount, int headerRowIndex, int dataRows) {
+        for (int c = 0; c < colCount; c++) {
+            double maxLen = displayLen(HEADERS[c]);
+            for (int r = 0; r < dataRows; r++) {
+                Row row = sheet.getRow(headerRowIndex + 1 + r);
+                Cell cell = row == null ? null : row.getCell(c);
+                if (cell != null && cell.getCellType() == CellType.STRING) {
+                    maxLen = Math.max(maxLen, displayLen(cell.getStringCellValue()));
+                }
+            }
+            double widthChars = Math.min(maxLen + COL_WIDTH_PADDING, COL_WIDTH_MAX + COL_WIDTH_PADDING);
+            // 涓枃鎸変袱涓崐瑙掑瓧绗﹀搴﹁绠�
+            sheet.setColumnWidth(c, (int) (widthChars * 256));
+        }
+    }
+
+    /** 璁$畻鏄剧ず瀹藉害锛氫腑鏂囩瓑鍏ㄨ瀛楃鎸� 2 涓崐瑙掑搴﹁ */
+    private static double displayLen(String s) {
+        if (s == null) {
+            return 0;
+        }
+        double len = 0;
+        for (char ch : s.toCharArray()) {
+            if ((ch >= 0x2E80 && ch <= 0x9FFF) || (ch >= 0x3000 && ch <= 0x303F) || ch >= 0xFF00) {
+                len += 2;
+            } else {
+                len += 1;
+            }
+        }
+        return len;
+    }
+
+    /** 璇诲彇 PNG 澶撮儴鐨勫/楂橈紙澶х鏁存暟锛� */
+    private static int readInt(byte[] b, int off) {
+        return ((b[off] & 0xFF) << 24) | ((b[off + 1] & 0xFF) << 16) | ((b[off + 2] & 0xFF) << 8) | (b[off + 3] & 0xFF);
+    }
+
+    private static CellStyle createStyle(XSSFWorkbook workbook, boolean bold, float fontSize,
+                                         HorizontalAlignment align) {
+        CellStyle style = workbook.createCellStyle();
+        Font font = workbook.createFont();
+        font.setFontName("瀹嬩綋");
+        font.setBold(bold);
+        font.setFontHeightInPoints((short) fontSize);
+        style.setFont(font);
+        style.setAlignment(align);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        return style;
+    }
+
+    private static void addThinBorder(CellStyle style) {
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+    }
+
+    private static void applyRegionBorder(Sheet sheet, CellRangeAddress region) {
+        RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+        RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+    }
+
+    private static void setText(Cell cell, String value) {
+        cell.setCellValue(value == null ? "" : value);
+    }
+
+    private static String plain(BigDecimal value) {
+        return value == null ? "" : value.toPlainString();
+    }
+}
diff --git a/src/main/resources/mapper/quality/QualityInspectMapper.xml b/src/main/resources/mapper/quality/QualityInspectMapper.xml
index ed01cad..d69932b 100644
--- a/src/main/resources/mapper/quality/QualityInspectMapper.xml
+++ b/src/main/resources/mapper/quality/QualityInspectMapper.xml
@@ -63,7 +63,7 @@
         <if test="qualityInspect.salesContractNo != null and qualityInspect.salesContractNo != '' ">
             AND po_sales.sales_contract_no like concat('%',#{qualityInspect.salesContractNo},'%')
         </if>
-        ORDER BY qi.check_time DESC
+        ORDER BY qi.check_time ASC, qi.id ASC
     </select>
 
     <select id="qualityInspectExport" resultType="com.ruoyi.quality.pojo.QualityInspect">
diff --git a/src/main/resources/static/junge-logo.png b/src/main/resources/static/junge-logo.png
new file mode 100644
index 0000000..72aa61f
--- /dev/null
+++ b/src/main/resources/static/junge-logo.png
Binary files differ

--
Gitblit v1.9.3