feat(excel): 优化Excel列宽与必填标记并支持巡检附件
- 优化表头列宽计算逻辑,支持中日韩宽字符占两格并预留边距
- 为各模块导入VO的必填字段添加 @ExcelRequired 注解
- 导出模板时自动注册 RequiredHeaderWriteHandler 标记必填表头
- 工艺路线导出模板各 Sheet 注册列宽自适应与必填表头处理器
- 后端质检附件接口与存储枚举新增 patrol 巡检类型支持
- 前端质检附件组件 qc-attachments 新增 patrol 类型兼容
| | |
| | | } |
| | | } |
| | | |
| | | /** 按 Excel 列宽估计表头:中日韩宽字符占两格,并为边距留出空间。 */ |
| | | private int headerWidth(String text) { |
| | | int widest = 0; |
| | | int line = 0; |
| | | for (int offset = 0; offset < text.length();) { |
| | | int cp = text.codePointAt(offset); |
| | | offset += Character.charCount(cp); |
| | | if (cp == '\n' || cp == '\r') { |
| | | widest = Math.max(widest, line); |
| | | line = 0; |
| | | continue; |
| | | } |
| | | Character.UnicodeScript script = Character.UnicodeScript.of(cp); |
| | | boolean wide = script == Character.UnicodeScript.HAN || script == Character.UnicodeScript.HIRAGANA |
| | | || script == Character.UnicodeScript.KATAKANA || script == Character.UnicodeScript.HANGUL |
| | | || (cp >= 0xff01 && cp <= 0xff60) || (cp >= 0xffe0 && cp <= 0xffe6); |
| | | line += wide ? 2 : 1; |
| | | } |
| | | return Math.max(widest, line); |
| | | } |
| | | |
| | | @SuppressWarnings("EnhancedSwitchMigration") |
| | | private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) { |
| | | if (isHead) { |
| | | return cell.getStringCellValue().getBytes().length; |
| | | return Math.max(12, headerWidth(cell.getStringCellValue()) + 3); |
| | | } |
| | | WriteCellData<?> cellData = cellDataList.get(0); |
| | | CellDataTypeEnum type = cellData.getType(); |
| | |
| | | import cn.idev.excel.metadata.CellExtra; |
| | | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; |
| | | import cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler; |
| | | import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | |
| | | public static <T> void write(HttpServletResponse response, String filename, String sheetName, |
| | | Class<T> head, List<T> data) throws IOException { |
| | | // 输出 Excel |
| | | ExcelWriter excelWriter = FastExcelFactory.write(response.getOutputStream(), head) |
| | | var builder = FastExcelFactory.write(response.getOutputStream(), head) |
| | | .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 |
| | | .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 |
| | | .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框 |
| | | .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 |
| | | .build(); |
| | | .registerConverter(new LongStringConverter()); // 避免 Long 类型丢失精度 |
| | | if (filename.contains("模板")) builder.registerWriteHandler(new RequiredHeaderWriteHandler()); |
| | | ExcelWriter excelWriter = builder.build(); |
| | | try { |
| | | excelWriter.write(data, FastExcelFactory.writerSheet(sheetName).build()); |
| | | } finally { |
| | |
| | | |
| | | @ExcelProperty("合同名称") |
| | | @Schema(description = "合同名称", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("客户名称") |
| | | @Schema(description = "客户名称,需与 CRM 客户名称一致", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String customerName; |
| | | |
| | | @ExcelProperty("下单时间") |
| | |
| | | public class CrmCustomerImportExcelVO { |
| | | |
| | | @ExcelProperty("客户名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("手机") |
| | |
| | | public class HrmAttendanceRecordImportExcelVO { |
| | | |
| | | @ExcelProperty("员工姓名") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String userName; |
| | | |
| | | @ExcelProperty("手机号") |
| | | private String phone; |
| | | |
| | | @ExcelProperty("考勤日期") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private LocalDate date; |
| | | |
| | | @ExcelProperty("上班打卡时间") |
| | |
| | | public class HrmEmployeeContractImportExcelVO { |
| | | |
| | | @ExcelProperty("员工姓名") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty(value = "合同类型", converter = DictConvert.class) |
| | | @DictFormat("hrm_contract_type") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private Integer contractType; |
| | | |
| | | @ExcelProperty(value = "合同期限类型", converter = DictConvert.class) |
| | | @DictFormat("hrm_contract_term_type") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private Integer contractTermType; |
| | | |
| | | @ExcelProperty("签约主体(公司全称)") |
| | |
| | | private LocalDate signDate; |
| | | |
| | | @ExcelProperty("合同开始日期") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private LocalDate startDate; |
| | | |
| | | @ExcelProperty("合同结束日期") |
| | |
| | | public class HrmEmployeeImportExcelVO { |
| | | |
| | | @ExcelProperty("员工姓名") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty(value = "性别", converter = DictConvert.class) |
| | |
| | | @NoArgsConstructor |
| | | public class MdmItemImportExcelVO { |
| | | |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("产品编号(修改时用于匹配,新增时自动生成)") |
| | | private String code; |
| | | |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("物料名称") |
| | | private String name; |
| | | |
| | |
| | | public class MdmUnitMeasureImportExcelVO { |
| | | |
| | | @ExcelProperty("单位编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String code; |
| | | |
| | | @ExcelProperty("单位名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("是否主单位") |
| | |
| | | public class MesDvMachineryImportExcelVO { |
| | | |
| | | @ExcelProperty("设备编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String code; |
| | | |
| | | @ExcelProperty("设备名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("品牌") |
| | |
| | | private String specification; |
| | | |
| | | @ExcelProperty("设备类型编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String machineryTypeCode; |
| | | |
| | | @ExcelProperty("所属车间编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String workshopCode; |
| | | |
| | | @ExcelProperty(value = "设备状态", converter = DictConvert.class) |
| | |
| | | |
| | | @ExcelProperty("计量器具编码") |
| | | @Schema(description = "计量器具编码,唯一", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String code; |
| | | |
| | | @ExcelProperty("计量器具名称") |
| | | @Schema(description = "计量器具名称", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty(value = "器具类别", converter = DictConvert.class) |
| | |
| | | public class MesMdItemTypeImportExcelVO { |
| | | |
| | | @ExcelProperty("分类编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String code; |
| | | |
| | | @ExcelProperty("分类名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("父分类编码") |
| | |
| | | ExcelWriter writer = FastExcelFactory.write(response.getOutputStream()).autoCloseStream(false).build(); |
| | | try { |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(0, "路线") |
| | | .head(MesProRouteCompleteImportWorkbook.RouteRow.class).build()); |
| | | .head(MesProRouteCompleteImportWorkbook.RouteRow.class) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy()) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(1, "产品") |
| | | .head(MesProRouteCompleteImportWorkbook.ProductRow.class).build()); |
| | | .head(MesProRouteCompleteImportWorkbook.ProductRow.class) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy()) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(2, "工序") |
| | | .head(MesProRouteCompleteImportWorkbook.ProcessRow.class).build()); |
| | | .head(MesProRouteCompleteImportWorkbook.ProcessRow.class) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy()) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(3, "工序产出") |
| | | .head(MesProRouteCompleteImportWorkbook.OutputRow.class).build()); |
| | | .head(MesProRouteCompleteImportWorkbook.OutputRow.class) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy()) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build()); |
| | | writer.write(Collections.emptyList(), FastExcelFactory.writerSheet(4, "工序投料") |
| | | .head(MesProRouteCompleteImportWorkbook.InputRow.class).build()); |
| | | .head(MesProRouteCompleteImportWorkbook.InputRow.class) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy()) |
| | | .registerWriteHandler(new cn.iocoder.yudao.framework.excel.core.handler.RequiredHeaderWriteHandler()).build()); |
| | | writer.write(List.of( |
| | | new MesProRouteCompleteImportWorkbook.GuideRow("路线", "每行一条路线;路线编码唯一。完整导入仅新增,已有编码会报错,导入后默认停用。"), |
| | | new MesProRouteCompleteImportWorkbook.GuideRow("产品", "路线编码与路线表一致;每条路线至少一个产品。物料、仓库、库区、库位填写已有业务编码;时间单位填 MINUTE/HOUR/DAY。"), |
| | |
| | | |
| | | @Data |
| | | public static class RouteRow { |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("路线编码") private String routeCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("路线名称") private String name; |
| | | @ExcelProperty("路线说明") private String description; |
| | | @ExcelProperty("备注") private String remark; |
| | |
| | | |
| | | @Data |
| | | public static class ProductRow { |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("路线编码") private String routeCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("产品物料编码") private String itemCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("仓库编码") private String warehouseCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("库区编码") private String locationCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("库位编码") private String areaCode; |
| | | @ExcelProperty("生产数量") private Integer quantity; |
| | | @ExcelProperty("生产用时") private BigDecimal productionTime; |
| | |
| | | |
| | | @Data |
| | | public static class ProcessRow { |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("路线编码") private String routeCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("工序编码") private String processCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("排序") private Integer sort; |
| | | @ExcelProperty("工艺参数模板ID") private Long paramTemplateId; |
| | | @ExcelProperty("工序收率") private BigDecimal yieldRate; |
| | |
| | | |
| | | @Data |
| | | public static class OutputRow { |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("路线编码") private String routeCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("工序编码") private String processCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("产出物料编码") private String itemCode; |
| | | @ExcelProperty("归属产品编码(空为通用)") private String productCode; |
| | | @ExcelProperty("排序") private Integer sort; |
| | |
| | | |
| | | @Data |
| | | public static class InputRow { |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("路线编码") private String routeCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("工序编码") private String processCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("归属产品编码") private String productCode; |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("投料物料编码") private String itemCode; |
| | | @ExcelProperty("用料比例") private BigDecimal quantity; |
| | | @ExcelProperty("备注") private String remark; |
| | |
| | | |
| | | @Data |
| | | public static class GuideRow { |
| | | @cn.idev.excel.annotation.write.style.ColumnWidth(16) |
| | | @ExcelProperty("工作表") private String sheet; |
| | | @cn.idev.excel.annotation.write.style.ColumnWidth(120) |
| | | @ExcelProperty("填写说明") private String description; |
| | | |
| | | public GuideRow(String sheet, String description) { |
| | |
| | | @NoArgsConstructor |
| | | public class MesProRouteImportExcelVO { |
| | | |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("工艺路线编码") |
| | | private String code; |
| | | |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | @ExcelProperty("工艺路线名称") |
| | | private String name; |
| | | |
| | |
| | | @Resource private MesQcIpqcService ipqcService; |
| | | @Resource private MesQcOqcService oqcService; |
| | | @Resource private MesQcRqcService rqcService; |
| | | @Resource private cn.iocoder.yudao.module.mes.controller.admin.qc.patrol.PatrolMapper patrolMapper; |
| | | @Resource private StorageAttachmentApi storageApi; |
| | | @Resource private org.springframework.jdbc.core.JdbcTemplate jdbcTemplate; |
| | | |
| | | @GetMapping("/list") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-' + #type.toLowerCase() + ':query')") |
| | | public CommonResult<List<StorageBlobRespDTO>> list( |
| | | @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc") String type, |
| | | @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") String type, |
| | | @RequestParam @NotNull Long id) { |
| | | validateDocument(type, id); |
| | | var files = storageApi.listAttachments(recordType(type), id); |
| | |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-' + #type.toLowerCase() + ':update')") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public CommonResult<Boolean> remove( |
| | | @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc") String type, |
| | | @RequestParam @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") String type, |
| | | @RequestParam @NotNull Long id, @RequestParam @NotNull Long blobId) { |
| | | lockDocument(type, id); |
| | | validateDocument(type, id); |
| | |
| | | |
| | | private String recordType(String type) { |
| | | return switch (type) { |
| | | case "iqc", "ipqc", "oqc", "rqc" -> "mes_qc_" + type + "_attachment"; |
| | | case "iqc", "ipqc", "oqc", "rqc", "patrol" -> "mes_qc_" + type + "_attachment"; |
| | | default -> throw new ServiceException(400, "质检类型无效"); |
| | | }; |
| | | } |
| | |
| | | case "ipqc" -> "mes_qc_ipqc"; |
| | | case "oqc" -> "mes_qc_oqc"; |
| | | case "rqc" -> "mes_qc_rqc"; |
| | | case "patrol" -> "mes_qc_patrol"; |
| | | default -> throw new ServiceException(400, "质检类型无效"); |
| | | }; |
| | | var ids = jdbcTemplate.queryForList("SELECT id FROM " + table + " WHERE id = ? AND deleted = 0 FOR UPDATE", Long.class, id); |
| | |
| | | case "ipqc" -> ipqcService.validateIpqcExists(id).getStatus(); |
| | | case "oqc" -> oqcService.validateOqcExists(id).getStatus(); |
| | | case "rqc" -> rqcService.validateRqcExists(id).getStatus(); |
| | | case "patrol" -> { if (patrolMapper.selectById(id) == null) throw new ServiceException(400, "巡检记录不存在"); yield 0; } |
| | | default -> throw new ServiceException(400, "质检类型无效"); |
| | | }; |
| | | } |
| | | |
| | | @Data public static class SaveReq { |
| | | @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc") private String type; |
| | | @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") private String type; |
| | | @NotNull private Long id; |
| | | @NotEmpty @Size(max = 50) private List<@NotNull Long> blobIds; |
| | | } |
| | | @Data public static class AppendReq { |
| | | @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc") private String type; |
| | | @NotNull @Pattern(regexp = "iqc|ipqc|oqc|rqc|patrol") private String type; |
| | | @NotNull private Long id; |
| | | @NotNull private Long blobId; |
| | | } |
| | |
| | | public class MesWmMaterialStockImportExcelVO { |
| | | |
| | | @ExcelProperty("物料编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String itemCode; |
| | | |
| | | @ExcelProperty("仓库") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String warehouse; |
| | | |
| | | @ExcelProperty("库区") |
| | |
| | | private String vendor; |
| | | |
| | | @ExcelProperty("操作类型") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String operationType; |
| | | |
| | | @ExcelProperty("在库数量") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private BigDecimal quantity; |
| | | |
| | | } |
| | |
| | | public class SrmSupplierImportExcelVO { |
| | | |
| | | @ExcelProperty("供应商编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String code; |
| | | |
| | | @ExcelProperty("供应商名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("供应商简称") |
| | |
| | | public class DeptImportExcelVO { |
| | | |
| | | @ExcelProperty("部门名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("上级部门全路径") |
| | | private String parentPath; |
| | | |
| | | @ExcelProperty("显示顺序") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private Integer sort; |
| | | |
| | | @ExcelProperty("联系电话") |
| | |
| | | |
| | | @ExcelProperty(value = "状态", converter = DictConvert.class) |
| | | @DictFormat(DictTypeConstants.COMMON_STATUS) |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private Integer status; |
| | | |
| | | } |
| | |
| | | public class PostImportExcelVO { |
| | | |
| | | @ExcelProperty("岗位名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String name; |
| | | |
| | | @ExcelProperty("岗位编码") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String code; |
| | | |
| | | @ExcelProperty("岗位排序") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private Integer sort; |
| | | |
| | | @ExcelProperty(value = "状态", converter = DictConvert.class) |
| | |
| | | public class UserImportExcelVO { |
| | | |
| | | @ExcelProperty("登录名称") |
| | | @cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired |
| | | private String username; |
| | | |
| | | @ExcelProperty("用户名称") |
| | |
| | | MES_QC_IQC_ATTACHMENT("mes_qc_iqc_attachment", "来料质检附件"), |
| | | MES_QC_IPQC_ATTACHMENT("mes_qc_ipqc_attachment", "过程质检附件"), |
| | | MES_QC_OQC_ATTACHMENT("mes_qc_oqc_attachment", "出货质检附件"), |
| | | MES_QC_PATROL_ATTACHMENT("mes_qc_patrol_attachment", "质量巡检附件"), |
| | | MES_QC_RQC_ATTACHMENT("mes_qc_rqc_attachment", "退货质检附件"), |
| | | |
| | | // ===== SRM ===== |