Pengxy пре 2 месеци
родитељ
комит
0b8a7225e2
24 измењених фајлова са 1545 додато и 162 уклоњено
  1. 11 8
      vite.config.ts
  2. 118 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/WorkOrderRoutingController.java
  3. 7 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderMaterialSaveReqVO.java
  4. 37 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderRoutingPageReqVO.java
  5. 126 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderRoutingRespVO.java
  6. 109 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderRoutingSaveReqVO.java
  7. 5 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderScheduleUpdateReqVO.java
  8. 22 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/convert/WorkOrderRoutingConvert.java
  9. 298 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/dal/dataobject/workorder/WorkOrderRoutingDO.java
  10. 74 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/dal/mysql/workorder/WorkOrderRoutingMapper.java
  11. 5 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/enums/ErrorCodeConstants.java
  12. 33 10
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/WorkOrderMaterialServiceImpl.java
  13. 91 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/WorkOrderRoutingService.java
  14. 155 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/WorkOrderRoutingServiceImpl.java
  15. 13 0
      yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/impl/WorkOrderScheduleServiceImpl.java
  16. 190 0
      yudao-module-makeplan/src/main/resources/mapper/workorder/WorkOrderRoutingMapper.xml
  17. 26 0
      yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java
  18. 1 1
      yudao-ui/yudao-ui-admin-vue3/src/api/jiaohuo/production.ts
  19. 51 0
      yudao-ui/yudao-ui-admin-vue3/src/api/makeplan/workorderRouting.ts
  20. 1 5
      yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/WorkOrderMaterial.vue
  21. 64 48
      yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/WorkOrderProcess.vue
  22. 8 6
      yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/components/PriorityAdjustForm.vue
  23. 99 77
      yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/components/WorkOrderProcessForm.vue
  24. 1 7
      yudao-ui/yudao-ui-admin-vue3/vite.config.ts

+ 11 - 8
vite.config.ts

@@ -63,25 +63,28 @@ export default ({command, mode}: ConfigEnv): UserConfig => {
             ]
         },
         build: {
-            minify: 'terser',
+            minify: 'esbuild', // 使用 esbuild 代替 terser,更快且内存占用更少
             outDir: env.VITE_OUT_DIR || 'dist',
             sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
             // brotliSize: false,
-            terserOptions: {
-                compress: {
-                    drop_debugger: env.VITE_DROP_DEBUGGER === 'true',
-                    drop_console: env.VITE_DROP_CONSOLE === 'true'
-                }
-            },
             rollupOptions: {
                 output: {
                     manualChunks: {
                       echarts: ['echarts'], // 将 echarts 单独打包,参考 https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/IAB1SX 讨论
                       'form-create': ['@form-create/element-ui'], // 参考 https://github.com/yudaocode/yudao-ui-admin-vue3/issues/148 讨论
                       'form-designer': ['@form-create/designer'],
-                    }
+                    },
+                    chunkFileNames: 'js/[name]-[hash].js',
+                    entryFileNames: 'js/[name]-[hash].js',
+                    assetFileNames: '[ext]/[name]-[hash].[ext]'
                 },
+                // 增加最大 chunk 大小限制
+                onwarn(warning, warn) {
+                    if (warning.code === 'CIRCULAR_DEPENDENCY') return
+                    warn(warning)
+                }
             },
+            chunkSizeWarningLimit: 2000 // 增加 chunk 大小警告限制
         },
         optimizeDeps: {include, exclude}
     }

+ 118 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/WorkOrderRoutingController.java

@@ -0,0 +1,118 @@
+package cn.iocoder.yudao.module.makeplan.controller.admin.workorder;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingPageReqVO;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingRespVO;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingSaveReqVO;
+import cn.iocoder.yudao.module.makeplan.convert.WorkOrderRoutingConvert;
+import cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderRoutingDO;
+import cn.iocoder.yudao.module.makeplan.service.WorkOrderRoutingService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+/**
+ * 工单工序明细 Controller
+ *
+ * @author 芋道源码
+ */
+@Tag(name = "管理后台 - 工单工序明细")
+@RestController
+@RequestMapping("/makeplan/workorder-routing")
+@Validated
+public class WorkOrderRoutingController {
+
+    @Resource
+    private WorkOrderRoutingService workOrderRoutingService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建工单工序明细")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:create')")
+    public CommonResult<Long> createWorkOrderRouting(@Valid @RequestBody WorkOrderRoutingSaveReqVO createReqVO) {
+        return success(workOrderRoutingService.createWorkOrderRouting(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新工单工序明细")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:update')")
+    public CommonResult<Boolean> updateWorkOrderRouting(@Valid @RequestBody WorkOrderRoutingSaveReqVO updateReqVO) {
+        workOrderRoutingService.updateWorkOrderRouting(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除工单工序明细")
+    @Parameter(name = "id", description = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:delete')")
+    public CommonResult<Boolean> deleteWorkOrderRouting(@RequestParam("id") Long id) {
+        workOrderRoutingService.deleteWorkOrderRouting(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得工单工序明细")
+    @Parameter(name = "id", description = "编号", required = true, example = "1")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:query')")
+    public CommonResult<WorkOrderRoutingRespVO> getWorkOrderRouting(@RequestParam("id") Long id) {
+        WorkOrderRoutingDO routing = workOrderRoutingService.getWorkOrderRouting(id);
+        return success(WorkOrderRoutingConvert.INSTANCE.convert(routing));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得工单工序明细分页")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:query')")
+    public CommonResult<PageResult<WorkOrderRoutingRespVO>> getWorkOrderRoutingPage(@Valid WorkOrderRoutingPageReqVO pageReqVO) {
+        PageResult<WorkOrderRoutingDO> pageResult = workOrderRoutingService.getWorkOrderRoutingPage(pageReqVO);
+        return success(new PageResult<>(
+                pageResult.getList().stream().map(WorkOrderRoutingConvert.INSTANCE::convert).toList(),
+                pageResult.getTotal()
+        ));
+    }
+
+    @GetMapping("/workorder-list")
+    @Operation(summary = "获取工单列表(用于下拉选择)")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:query')")
+    public CommonResult<List<Map<String, String>>> getWorkOrderList() {
+        return success(workOrderRoutingService.getWorkOrderList());
+    }
+
+    @GetMapping("/workcenter-list")
+    @Operation(summary = "获取工作中心列表(用于下拉选择)")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:query')")
+    public CommonResult<List<Map<String, String>>> getWorkCenterList() {
+        return success(workOrderRoutingService.getWorkCenterList());
+    }
+
+    @GetMapping("/production-line-list")
+    @Operation(summary = "获取生产线列表(用于下拉选择)")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:query')")
+    public CommonResult<List<Map<String, String>>> getProductionLineList() {
+        return success(workOrderRoutingService.getProductionLineList());
+    }
+
+    @GetMapping("/supplier-list")
+    @Operation(summary = "获取供应商列表(用于下拉选择)")
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:query')")
+    public CommonResult<List<Map<String, String>>> getSupplierList() {
+        return success(workOrderRoutingService.getSupplierList());
+    }
+
+    @GetMapping("/workorder-info")
+    @Operation(summary = "获取工单信息")
+    @Parameter(name = "workOrd", description = "工单编号", required = true)
+    @PreAuthorize("@ss.hasPermission('makeplan:workorder-routing:query')")
+    public CommonResult<Map<String, String>> getWorkOrderInfo(@RequestParam("workOrd") String workOrd) {
+        return success(workOrderRoutingService.getWorkOrderInfo(workOrd));
+    }
+}

+ 7 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderMaterialSaveReqVO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo;
 
+import com.mzt.logapi.starter.annotation.DiffLogField;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotBlank;
 import jakarta.validation.constraints.NotNull;
@@ -17,6 +18,7 @@ public class WorkOrderMaterialSaveReqVO {
 
     @Schema(description = "工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "WO202401001")
     @NotBlank(message = "工单编号不能为空")
+    @DiffLogField(name = "工单编号")
     private String workOrd;
 
     @Schema(description = "行号", example = "1")
@@ -24,19 +26,24 @@ public class WorkOrderMaterialSaveReqVO {
 
     @Schema(description = "物料编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "MAT001")
     @NotBlank(message = "物料编码不能为空")
+    @DiffLogField(name = "物料编码")
     private String itemNum;
 
     @Schema(description = "工序", example = "OP10")
+    @DiffLogField(name = "工序")
     private String op;
 
     @Schema(description = "库位", example = "A01")
+    @DiffLogField(name = "库位")
     private String location;
 
     @Schema(description = "批序号", example = "LOT001")
+    @DiffLogField(name = "批序号")
     private String lotSerial;
 
     @Schema(description = "需求数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
     @NotNull(message = "需求数量不能为空")
+    @DiffLogField(name = "需求数量")
     private BigDecimal qtyRequired;
 
     @Schema(description = "已分配数量", example = "50.00")

+ 37 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderRoutingPageReqVO.java

@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - 工单工序明细分页请求 VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class WorkOrderRoutingPageReqVO extends PageParam {
+
+    @Schema(description = "生产指令", example = "M500000001")
+    private String workOrd;
+
+    @Schema(description = "生产批次", example = "LOT001")
+    private String lotSerial;
+
+    @Schema(description = "物料编号", example = "MAT001")
+    private String itemNum;
+
+    @Schema(description = "班组", example = "A班")
+    private String site;
+
+    @Schema(description = "工单创建时间-开始")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime createTimeStart;
+
+    @Schema(description = "工单更新时间-开始")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime updateTimeStart;
+}

+ 126 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderRoutingRespVO.java

@@ -0,0 +1,126 @@
+package cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 工单工序明细响应 VO")
+@Data
+public class WorkOrderRoutingRespVO {
+
+    @Schema(description = "主键ID", example = "1")
+    private Long id;
+
+    @Schema(description = "生产指令", example = "M500000001")
+    private String workOrd;
+
+    @Schema(description = "生产批次", example = "LOT001")
+    private String batch;
+
+    @Schema(description = "生产批号", example = "LOT001")
+    private String lotSerial;
+
+    @Schema(description = "下达日期")
+    private LocalDateTime eff;
+
+    @Schema(description = "物料编号", example = "MAT001")
+    private String itemNum;
+
+    @Schema(description = "物料名称", example = "产品A")
+    private String itemName;
+
+    @Schema(description = "工序", example = "OP10")
+    private String op;
+
+    @Schema(description = "工序名称", example = "加工")
+    private String opName;
+
+    @Schema(description = "关键工序 (0-否, 1-是)", example = "1")
+    private Integer milestoneOp;
+
+    @Schema(description = "完成数量", example = "50.00")
+    private BigDecimal qtyComplete;
+
+    @Schema(description = "订单数量", example = "100.00")
+    private BigDecimal qtyOrded;
+
+    @Schema(description = "工序委外 (0-否, 1-是)", example = "0")
+    private Integer processOut;
+
+    @Schema(description = "设备名称", example = "设备A")
+    private String machineName;
+
+    @Schema(description = "标准人数", example = "2")
+    private Integer standardStaffCount;
+
+    @Schema(description = "班组", example = "A班")
+    private String site;
+
+    @Schema(description = "产能(小时)", example = "10.00")
+    private BigDecimal rate;
+
+    @Schema(description = "工单创建时间")
+    private LocalDateTime createTime;
+
+    @Schema(description = "工单更新时间")
+    private LocalDateTime updateTime;
+
+    @Schema(description = "工序名称(用于表单)", example = "加工")
+    private String descr;
+
+    @Schema(description = "工作中心", example = "WC01")
+    private String workCtr;
+
+    @Schema(description = "生产线", example = "LINE01")
+    private String prodLine;
+
+    @Schema(description = "准备时间(小时)", example = "1.00")
+    private BigDecimal stdSetupTime;
+
+    @Schema(description = "供应商", example = "SUPP001")
+    private String processOutSupp;
+
+    @Schema(description = "委外提前期", example = "3")
+    private Integer processOutDay;
+
+    @Schema(description = "设备类型编码", example = "EQ001")
+    private String machine;
+
+    @Schema(description = "模具类型编码", example = "MOLD001")
+    private String toolCode;
+
+    @Schema(description = "标准人数(用于表单)", example = "2")
+    private Integer runCrew;
+
+    @Schema(description = "人员技能编码", example = "SKILL001")
+    private String engineer;
+
+    @Schema(description = "单位标准产能(小时)", example = "10.00")
+    private BigDecimal machBdnRate;
+
+    @Schema(description = "工序类型", example = "M")
+    private String workCode;
+
+    @Schema(description = "活动一准备时间(秒)", example = "300.00")
+    private BigDecimal setup;
+
+    @Schema(description = "活动二机器时间(秒)", example = "600.00")
+    private BigDecimal machinesPerOp;
+
+    @Schema(description = "活动三人工时间(秒)", example = "400.00")
+    private BigDecimal labor;
+
+    @Schema(description = "作业编码", example = "CHARGE001")
+    private String chargeCode;
+
+    @Schema(description = "开始日期")
+    private LocalDateTime startDate;
+
+    @Schema(description = "到期日期")
+    private LocalDateTime dueDate;
+
+    @Schema(description = "状态", example = "P")
+    private String status;
+}

+ 109 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderRoutingSaveReqVO.java

@@ -0,0 +1,109 @@
+package cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo;
+
+import com.mzt.logapi.starter.annotation.DiffLogField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "管理后台 - 工单工序明细创建/更新请求 VO")
+@Data
+public class WorkOrderRoutingSaveReqVO {
+
+    @Schema(description = "主键ID(更新时必填)", example = "1")
+    private Long id;
+
+    @Schema(description = "工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "M500000001")
+    @NotBlank(message = "工单编号不能为空")
+    @DiffLogField(name = "工单编号")
+    private String workOrd;
+
+    @Schema(description = "物料编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "MAT001")
+    @NotBlank(message = "物料编号不能为空")
+    @DiffLogField(name = "物料编号")
+    private String itemNum;
+
+    @Schema(description = "工序", requiredMode = Schema.RequiredMode.REQUIRED, example = "OP10")
+    @NotBlank(message = "工序不能为空")
+    @DiffLogField(name = "工序")
+    private String op;
+
+    @Schema(description = "工序名称", example = "加工")
+    @DiffLogField(name = "工序名称")
+    private String descr;
+
+    @Schema(description = "工作中心", example = "WC01")
+    @DiffLogField(name = "工作中心")
+    private String workCtr;
+
+    @Schema(description = "生产线", example = "LINE01")
+    @DiffLogField(name = "生产线")
+    private String prodLine;
+
+    @Schema(description = "准备时间(小时)", example = "1.00")
+    @DiffLogField(name = "准备时间")
+    private BigDecimal stdSetupTime;
+
+    @Schema(description = "需求数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
+    @NotNull(message = "需求数量不能为空")
+    @DiffLogField(name = "需求数量")
+    private BigDecimal qtyOrded;
+
+    @Schema(description = "关键工序 (0-否, 1-是)", example = "1")
+    @DiffLogField(name = "关键工序")
+    private Integer milestoneOp;
+
+    @Schema(description = "工序委外 (0-否, 1-是)", example = "0")
+    @DiffLogField(name = "工序委外")
+    private Integer processOut;
+
+    @Schema(description = "供应商", example = "SUPP001")
+    @DiffLogField(name = "供应商")
+    private String processOutSupp;
+
+    @Schema(description = "委外提前期", example = "3")
+    @DiffLogField(name = "委外提前期")
+    private Integer processOutDay;
+
+    @Schema(description = "设备类型编码", example = "EQ001")
+    @DiffLogField(name = "设备类型编码")
+    private String machine;
+
+    @Schema(description = "模具类型编码", example = "MOLD001")
+    @DiffLogField(name = "模具类型编码")
+    private String toolCode;
+
+    @Schema(description = "标准人数", example = "2")
+    @DiffLogField(name = "标准人数")
+    private Integer runCrew;
+
+    @Schema(description = "人员技能编码", example = "SKILL001")
+    @DiffLogField(name = "人员技能编码")
+    private String engineer;
+
+    @Schema(description = "单位标准产能(小时)", example = "10.00")
+    @DiffLogField(name = "单位标准产能")
+    private BigDecimal machBdnRate;
+
+    @Schema(description = "工序类型 (M-机械, P-人工)", example = "M")
+    @DiffLogField(name = "工序类型")
+    private String workCode;
+
+    @Schema(description = "活动一准备时间(秒)", example = "300.00")
+    @DiffLogField(name = "活动一准备时间")
+    private BigDecimal setup;
+
+    @Schema(description = "活动二机器时间(秒)", example = "600.00")
+    @DiffLogField(name = "活动二机器时间")
+    private BigDecimal machinesPerOp;
+
+    @Schema(description = "活动三人工时间(秒)", example = "400.00")
+    @DiffLogField(name = "活动三人工时间")
+    private BigDecimal labor;
+
+    @Schema(description = "作业编码", example = "CHARGE001")
+    @DiffLogField(name = "作业编码")
+    private String chargeCode;
+}

+ 5 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/controller/admin/workorder/vo/WorkOrderScheduleUpdateReqVO.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo;
 
+import com.mzt.logapi.starter.annotation.DiffLogField;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import lombok.Data;
@@ -15,14 +16,18 @@ public class WorkOrderScheduleUpdateReqVO {
     private Long id;
 
     @Schema(description = "优先级")
+    @DiffLogField(name = "优先级")
     private Integer priority;
 
     @Schema(description = "订单数量")
+    @DiffLogField(name = "订单数量")
     private BigDecimal qtyOrded;
 
     @Schema(description = "批号")
+    @DiffLogField(name = "批号")
     private String batch;
 
     @Schema(description = "备注")
+    @DiffLogField(name = "备注")
     private String remark;
 }

+ 22 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/convert/WorkOrderRoutingConvert.java

@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.makeplan.convert;
+
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingRespVO;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingSaveReqVO;
+import cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderRoutingDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+/**
+ * 工单工序明细 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface WorkOrderRoutingConvert {
+
+    WorkOrderRoutingConvert INSTANCE = Mappers.getMapper(WorkOrderRoutingConvert.class);
+
+    WorkOrderRoutingDO convert(WorkOrderRoutingSaveReqVO bean);
+
+    WorkOrderRoutingRespVO convert(WorkOrderRoutingDO bean);
+}

+ 298 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/dal/dataobject/workorder/WorkOrderRoutingDO.java

@@ -0,0 +1,298 @@
+package cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 工单工序明细 DO
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "WorkOrdRouting", autoResultMap = true)
+@Data
+public class WorkOrderRoutingDO {
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "RecID", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 工单编号
+     */
+    @TableField("WorkOrd")
+    private String workOrd;
+
+    /**
+     * 工单主表ID
+     */
+    @TableField("WorkOrdMasterRecID")
+    private Long workOrdMasterRecId;
+
+    /**
+     * 物料编号
+     */
+    @TableField("ItemNum")
+    private String itemNum;
+
+    /**
+     * 工序
+     */
+    @TableField("OP")
+    private String op;
+
+    /**
+     * 工序名称
+     */
+    @TableField("Descr")
+    private String descr;
+
+    /**
+     * 工作中心
+     */
+    @TableField("WorkCtr")
+    private String workCtr;
+
+    /**
+     * 生产线
+     */
+    @TableField("ProdLine")
+    private String prodLine;
+
+    /**
+     * 准备时间(小时)
+     */
+    @TableField("StdSetupTime")
+    private BigDecimal stdSetupTime;
+
+    /**
+     * 需求数量
+     */
+    @TableField("QtyOrded")
+    private BigDecimal qtyOrded;
+
+    /**
+     * 完成数量
+     */
+    @TableField("QtyComplete")
+    private BigDecimal qtyComplete;
+
+    /**
+     * 关键工序 (0-否, 1-是)
+     */
+    @TableField("MilestoneOp")
+    private Integer milestoneOp;
+
+    /**
+     * 工序委外 (0-否, 1-是)
+     */
+    @TableField("ProcessOut")
+    private Integer processOut;
+
+    /**
+     * 供应商
+     */
+    @TableField("ProcessOutSupp")
+    private String processOutSupp;
+
+    /**
+     * 委外提前期
+     */
+    @TableField("ProcessOutDay")
+    private Integer processOutDay;
+
+    /**
+     * 设备类型编码
+     */
+    @TableField("Machine")
+    private String machine;
+
+    /**
+     * 模具类型编码
+     */
+    @TableField("ToolCode")
+    private String toolCode;
+
+    /**
+     * 标准人数
+     */
+    @TableField("RunCrew")
+    private Integer runCrew;
+
+    /**
+     * 人员技能编码
+     */
+    @TableField("Engineer")
+    private String engineer;
+
+    /**
+     * 单位标准产能(小时)
+     */
+    @TableField("MachBdnRate")
+    private BigDecimal machBdnRate;
+
+    /**
+     * 工序类型 (M-机械, P-人工)
+     */
+    @TableField("WorkCode")
+    private String workCode;
+
+    /**
+     * 活动一准备时间(秒)
+     */
+    @TableField("Setup")
+    private BigDecimal setup;
+
+    /**
+     * 活动二机器时间(秒)
+     */
+    @TableField("MachinesperOp")
+    private BigDecimal machinesPerOp;
+
+    /**
+     * 活动三人工时间(秒)
+     */
+    @TableField("Labor")
+    private BigDecimal labor;
+
+    /**
+     * 作业编码
+     */
+    @TableField("ChargeCode")
+    private String chargeCode;
+
+    /**
+     * 开始日期
+     */
+    @TableField("StartDate")
+    private LocalDateTime startDate;
+
+    /**
+     * 到期日期
+     */
+    @TableField("DueDate")
+    private LocalDateTime dueDate;
+
+    /**
+     * 状态
+     */
+    @TableField("Status")
+    private String status;
+
+    /**
+     * 是否激活 (0-否, 1-是)
+     */
+    @TableField("IsActive")
+    private Integer isActive;
+
+    /**
+     * 域名
+     */
+    @TableField("Domain")
+    private String domain;
+
+    /**
+     * 地点
+     */
+    @TableField("Site")
+    private String site;
+
+    // ========== 关联查询字段 ==========
+
+    /**
+     * 物料名称
+     */
+    @TableField(exist = false)
+    private String itemName;
+
+    /**
+     * 工序名称
+     */
+    @TableField(exist = false)
+    private String opName;
+
+    /**
+     * 批次
+     */
+    @TableField(exist = false)
+    private String batch;
+
+    /**
+     * 下达日期
+     */
+    @TableField(exist = false)
+    private LocalDateTime eff;
+
+    /**
+     * 生产批次
+     */
+    @TableField(exist = false)
+    private String lotSerial;
+
+    /**
+     * 产能(小时)
+     */
+    @TableField(exist = false)
+    private BigDecimal rate;
+
+    /**
+     * 标准人数
+     */
+    @TableField(exist = false)
+    private Integer standardStaffCount;
+
+    /**
+     * 工序类型
+     */
+    @TableField(exist = false)
+    private String opType;
+
+    /**
+     * 设备类型编码
+     */
+    @TableField(exist = false)
+    private String internalEquipmentCode;
+
+    /**
+     * 模具类型编码
+     */
+    @TableField(exist = false)
+    private String moldTypeCode;
+
+    /**
+     * 人员技能编码
+     */
+    @TableField(exist = false)
+    private String skillNo;
+
+    /**
+     * 准备时间
+     */
+    @TableField(exist = false)
+    private BigDecimal setupTime;
+
+    /**
+     * 工单创建时间
+     */
+    @TableField(exist = false)
+    private LocalDateTime createTime;
+
+    /**
+     * 工单更新时间
+     */
+    @TableField(exist = false)
+    private LocalDateTime updateTime;
+
+    /**
+     * 设备名称
+     */
+    @TableField(exist = false)
+    private String machineName;
+}

+ 74 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/dal/mysql/workorder/WorkOrderRoutingMapper.java

@@ -0,0 +1,74 @@
+package cn.iocoder.yudao.module.makeplan.dal.mysql.workorder;
+
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingPageReqVO;
+import cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderRoutingDO;
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 工单工序明细 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+@InterceptorIgnore(tenantLine = "true")
+public interface WorkOrderRoutingMapper extends BaseMapper<WorkOrderRoutingDO> {
+
+    /**
+     * 分页查询工单工序明细(多表关联)
+     */
+    IPage<WorkOrderRoutingDO> selectPageWithJoin(Page<WorkOrderRoutingDO> page, 
+                                                   @Param("reqVO") WorkOrderRoutingPageReqVO reqVO);
+
+    /**
+     * 根据ID查询(忽略租户)
+     */
+    WorkOrderRoutingDO selectByIdIgnoreTenant(@Param("id") Long id);
+
+    /**
+     * 根据ID更新(忽略租户)
+     */
+    int updateByIdIgnoreTenant(@Param("entity") WorkOrderRoutingDO entity);
+
+    /**
+     * 获取工单列表(用于下拉选择)
+     */
+    List<Map<String, String>> selectWorkOrderList();
+
+    /**
+     * 获取工作中心列表(用于下拉选择)
+     */
+    List<Map<String, String>> selectWorkCenterList();
+
+    /**
+     * 获取生产线列表(用于下拉选择)
+     */
+    List<Map<String, String>> selectProductionLineList();
+
+    /**
+     * 获取供应商列表(用于下拉选择)
+     */
+    List<Map<String, String>> selectSupplierList();
+
+    /**
+     * 检查工单是否已有相同工序
+     */
+    int checkDuplicateOp(@Param("workOrd") String workOrd, @Param("op") String op, @Param("excludeId") Long excludeId);
+
+    /**
+     * 检查工序是否有完成数量
+     */
+    int checkHasCompleteQty(@Param("id") Long id);
+
+    /**
+     * 获取工单信息
+     */
+    Map<String, String> selectWorkOrderInfo(@Param("workOrd") String workOrd);
+}

+ 5 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/enums/ErrorCodeConstants.java

@@ -14,4 +14,9 @@ public interface ErrorCodeConstants {
 
     // ========== 工单物料明细 1-003-002-000 ==========
     ErrorCode WORK_ORDER_MATERIAL_NOT_EXISTS = new ErrorCode(1_003_002_001, "工单物料明细不存在");
+
+    // ========== 工单工序明细 1-003-003-000 ==========
+    ErrorCode WORK_ORDER_ROUTING_NOT_EXISTS = new ErrorCode(1_003_003_001, "工单工序明细不存在");
+    ErrorCode WORK_ORDER_ROUTING_OP_EXISTS = new ErrorCode(1_003_003_002, "工单已存在相同工序");
+    ErrorCode WORK_ORDER_ROUTING_HAS_COMPLETE_QTY = new ErrorCode(1_003_003_003, "工序已有完成数量,不允许修改或删除");
 }

+ 33 - 10
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/WorkOrderMaterialServiceImpl.java

@@ -1,17 +1,22 @@
 package cn.iocoder.yudao.module.makeplan.service;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderMaterialPageReqVO;
 import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderMaterialSaveReqVO;
 import cn.iocoder.yudao.module.makeplan.convert.WorkOrderMaterialConvert;
 import cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderMaterialDO;
 import cn.iocoder.yudao.module.makeplan.dal.mysql.workorder.WorkOrderMaterialMapper;
+import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.service.impl.DiffParseFunction;
+import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.makeplan.enums.ErrorCodeConstants.WORK_ORDER_MATERIAL_NOT_EXISTS;
+import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
 
 /**
  * 工单物料明细 Service 实现类
@@ -26,28 +31,54 @@ public class WorkOrderMaterialServiceImpl implements WorkOrderMaterialService {
     private WorkOrderMaterialMapper workOrderMaterialMapper;
 
     @Override
+    @LogRecord(type = WORK_ORDER_MATERIAL_TYPE, subType = WORK_ORDER_MATERIAL_CREATE_SUB_TYPE, 
+            bizNo = "{{#material.id}}", success = WORK_ORDER_MATERIAL_CREATE_SUCCESS)
     public Long createWorkOrderMaterial(WorkOrderMaterialSaveReqVO createReqVO) {
         // 插入
         WorkOrderMaterialDO material = WorkOrderMaterialConvert.INSTANCE.convert(createReqVO);
         material.setIsActive(1); // 默认激活状态
         workOrderMaterialMapper.insert(material);
+        
+        // 记录操作日志上下文
+        LogRecordContext.putVariable("material", material);
+        
         // 返回
         return material.getId();
     }
 
     @Override
+    @LogRecord(type = WORK_ORDER_MATERIAL_TYPE, subType = WORK_ORDER_MATERIAL_UPDATE_SUB_TYPE, 
+            bizNo = "{{#updateReqVO.id}}", success = WORK_ORDER_MATERIAL_UPDATE_SUCCESS)
     public void updateWorkOrderMaterial(WorkOrderMaterialSaveReqVO updateReqVO) {
         // 校验存在 - 使用自定义查询
-        validateWorkOrderMaterialExists(updateReqVO.getId());
+        WorkOrderMaterialDO oldMaterial = workOrderMaterialMapper.selectByIdIgnoreTenant(updateReqVO.getId());
+        if (oldMaterial == null) {
+            throw exception(WORK_ORDER_MATERIAL_NOT_EXISTS);
+        }
+        
+        // 记录操作日志上下文 - 用于DIFF对比
+        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldMaterial, WorkOrderMaterialSaveReqVO.class));
+        LogRecordContext.putVariable("updateReqVO", updateReqVO);
+        LogRecordContext.putVariable("material", oldMaterial);
+        
         // 更新 - 使用自定义更新方法
         WorkOrderMaterialDO updateObj = WorkOrderMaterialConvert.INSTANCE.convert(updateReqVO);
         workOrderMaterialMapper.updateByIdIgnoreTenant(updateObj);
     }
 
     @Override
+    @LogRecord(type = WORK_ORDER_MATERIAL_TYPE, subType = WORK_ORDER_MATERIAL_DELETE_SUB_TYPE, 
+            bizNo = "{{#id}}", success = WORK_ORDER_MATERIAL_DELETE_SUCCESS)
     public void deleteWorkOrderMaterial(Long id) {
         // 校验存在
-        validateWorkOrderMaterialExists(id);
+        WorkOrderMaterialDO material = workOrderMaterialMapper.selectByIdIgnoreTenant(id);
+        if (material == null) {
+            throw exception(WORK_ORDER_MATERIAL_NOT_EXISTS);
+        }
+        
+        // 记录操作日志上下文
+        LogRecordContext.putVariable("material", material);
+        
         // 逻辑删除 - 设置IsActive为0,使用自定义更新方法
         WorkOrderMaterialDO updateObj = new WorkOrderMaterialDO();
         updateObj.setId(id);
@@ -55,14 +86,6 @@ public class WorkOrderMaterialServiceImpl implements WorkOrderMaterialService {
         workOrderMaterialMapper.updateByIdIgnoreTenant(updateObj);
     }
 
-    private void validateWorkOrderMaterialExists(Long id) {
-        // 使用自定义查询方法,避免多租户拦截器影响
-        WorkOrderMaterialDO material = workOrderMaterialMapper.selectByIdIgnoreTenant(id);
-        if (material == null) {
-            throw exception(WORK_ORDER_MATERIAL_NOT_EXISTS);
-        }
-    }
-
     @Override
     public WorkOrderMaterialDO getWorkOrderMaterial(Long id) {
         return workOrderMaterialMapper.selectByIdIgnoreTenant(id);

+ 91 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/WorkOrderRoutingService.java

@@ -0,0 +1,91 @@
+package cn.iocoder.yudao.module.makeplan.service;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingPageReqVO;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingSaveReqVO;
+import cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderRoutingDO;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 工单工序明细 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface WorkOrderRoutingService {
+
+    /**
+     * 创建工单工序明细
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createWorkOrderRouting(WorkOrderRoutingSaveReqVO createReqVO);
+
+    /**
+     * 更新工单工序明细
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateWorkOrderRouting(WorkOrderRoutingSaveReqVO updateReqVO);
+
+    /**
+     * 删除工单工序明细
+     *
+     * @param id 编号
+     */
+    void deleteWorkOrderRouting(Long id);
+
+    /**
+     * 获得工单工序明细
+     *
+     * @param id 编号
+     * @return 工单工序明细
+     */
+    WorkOrderRoutingDO getWorkOrderRouting(Long id);
+
+    /**
+     * 获得工单工序明细分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 工单工序明细分页
+     */
+    PageResult<WorkOrderRoutingDO> getWorkOrderRoutingPage(WorkOrderRoutingPageReqVO pageReqVO);
+
+    /**
+     * 获取工单列表(用于下拉选择)
+     *
+     * @return 工单列表
+     */
+    List<Map<String, String>> getWorkOrderList();
+
+    /**
+     * 获取工作中心列表(用于下拉选择)
+     *
+     * @return 工作中心列表
+     */
+    List<Map<String, String>> getWorkCenterList();
+
+    /**
+     * 获取生产线列表(用于下拉选择)
+     *
+     * @return 生产线列表
+     */
+    List<Map<String, String>> getProductionLineList();
+
+    /**
+     * 获取供应商列表(用于下拉选择)
+     *
+     * @return 供应商列表
+     */
+    List<Map<String, String>> getSupplierList();
+
+    /**
+     * 获取工单信息
+     *
+     * @param workOrd 工单编号
+     * @return 工单信息
+     */
+    Map<String, String> getWorkOrderInfo(String workOrd);
+}

+ 155 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/WorkOrderRoutingServiceImpl.java

@@ -0,0 +1,155 @@
+package cn.iocoder.yudao.module.makeplan.service;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingPageReqVO;
+import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.WorkOrderRoutingSaveReqVO;
+import cn.iocoder.yudao.module.makeplan.convert.WorkOrderRoutingConvert;
+import cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderRoutingDO;
+import cn.iocoder.yudao.module.makeplan.dal.mysql.workorder.WorkOrderRoutingMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.service.impl.DiffParseFunction;
+import com.mzt.logapi.starter.annotation.LogRecord;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.makeplan.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
+
+/**
+ * 工单工序明细 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class WorkOrderRoutingServiceImpl implements WorkOrderRoutingService {
+
+    @Resource
+    private WorkOrderRoutingMapper workOrderRoutingMapper;
+
+    @Override
+    @LogRecord(type = WORK_ORDER_ROUTING_TYPE, subType = WORK_ORDER_ROUTING_CREATE_SUB_TYPE,
+            bizNo = "{{#routing.id}}", success = WORK_ORDER_ROUTING_CREATE_SUCCESS)
+    public Long createWorkOrderRouting(WorkOrderRoutingSaveReqVO createReqVO) {
+        // 校验工单是否已有相同工序
+        int count = workOrderRoutingMapper.checkDuplicateOp(createReqVO.getWorkOrd(), createReqVO.getOp(), null);
+        if (count > 0) {
+            throw exception(WORK_ORDER_ROUTING_OP_EXISTS);
+        }
+
+        // 插入
+        WorkOrderRoutingDO routing = WorkOrderRoutingConvert.INSTANCE.convert(createReqVO);
+        routing.setIsActive(1); // 默认激活状态
+        workOrderRoutingMapper.insert(routing);
+
+        // 记录操作日志上下文
+        LogRecordContext.putVariable("routing", routing);
+
+        // 返回
+        return routing.getId();
+    }
+
+    @Override
+    @LogRecord(type = WORK_ORDER_ROUTING_TYPE, subType = WORK_ORDER_ROUTING_UPDATE_SUB_TYPE,
+            bizNo = "{{#updateReqVO.id}}", success = WORK_ORDER_ROUTING_UPDATE_SUCCESS)
+    public void updateWorkOrderRouting(WorkOrderRoutingSaveReqVO updateReqVO) {
+        // 校验存在 - 使用自定义查询
+        WorkOrderRoutingDO oldRouting = workOrderRoutingMapper.selectByIdIgnoreTenant(updateReqVO.getId());
+        if (oldRouting == null) {
+            throw exception(WORK_ORDER_ROUTING_NOT_EXISTS);
+        }
+
+        // 校验工单是否已有相同工序(排除当前记录)
+        int count = workOrderRoutingMapper.checkDuplicateOp(updateReqVO.getWorkOrd(), updateReqVO.getOp(), updateReqVO.getId());
+        if (count > 0) {
+            throw exception(WORK_ORDER_ROUTING_OP_EXISTS);
+        }
+
+        // 校验工序是否有完成数量
+        int hasCompleteQty = workOrderRoutingMapper.checkHasCompleteQty(updateReqVO.getId());
+        if (hasCompleteQty > 0) {
+            throw exception(WORK_ORDER_ROUTING_HAS_COMPLETE_QTY);
+        }
+
+        // 记录操作日志上下文 - 用于DIFF对比
+        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldRouting, WorkOrderRoutingSaveReqVO.class));
+        LogRecordContext.putVariable("updateReqVO", updateReqVO);
+        LogRecordContext.putVariable("routing", oldRouting);
+
+        // 更新 - 使用自定义更新方法
+        WorkOrderRoutingDO updateObj = WorkOrderRoutingConvert.INSTANCE.convert(updateReqVO);
+        workOrderRoutingMapper.updateByIdIgnoreTenant(updateObj);
+    }
+
+    @Override
+    @LogRecord(type = WORK_ORDER_ROUTING_TYPE, subType = WORK_ORDER_ROUTING_DELETE_SUB_TYPE,
+            bizNo = "{{#id}}", success = WORK_ORDER_ROUTING_DELETE_SUCCESS)
+    public void deleteWorkOrderRouting(Long id) {
+        // 校验存在
+        WorkOrderRoutingDO routing = workOrderRoutingMapper.selectByIdIgnoreTenant(id);
+        if (routing == null) {
+            throw exception(WORK_ORDER_ROUTING_NOT_EXISTS);
+        }
+
+        // 校验工序是否有完成数量
+        int hasCompleteQty = workOrderRoutingMapper.checkHasCompleteQty(id);
+        if (hasCompleteQty > 0) {
+            throw exception(WORK_ORDER_ROUTING_HAS_COMPLETE_QTY);
+        }
+
+        // 记录操作日志上下文
+        LogRecordContext.putVariable("routing", routing);
+
+        // 逻辑删除 - 设置IsActive为0,使用自定义更新方法
+        WorkOrderRoutingDO updateObj = new WorkOrderRoutingDO();
+        updateObj.setId(id);
+        updateObj.setIsActive(0);
+        workOrderRoutingMapper.updateByIdIgnoreTenant(updateObj);
+    }
+
+    @Override
+    public WorkOrderRoutingDO getWorkOrderRouting(Long id) {
+        return workOrderRoutingMapper.selectByIdIgnoreTenant(id);
+    }
+
+    @Override
+    public PageResult<WorkOrderRoutingDO> getWorkOrderRoutingPage(WorkOrderRoutingPageReqVO pageReqVO) {
+        // 使用自定义SQL进行多表关联查询
+        Page<WorkOrderRoutingDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
+        com.baomidou.mybatisplus.core.metadata.IPage<WorkOrderRoutingDO> result =
+                workOrderRoutingMapper.selectPageWithJoin(page, pageReqVO);
+        return new PageResult<>(result.getRecords(), result.getTotal());
+    }
+
+    @Override
+    public List<Map<String, String>> getWorkOrderList() {
+        return workOrderRoutingMapper.selectWorkOrderList();
+    }
+
+    @Override
+    public List<Map<String, String>> getWorkCenterList() {
+        return workOrderRoutingMapper.selectWorkCenterList();
+    }
+
+    @Override
+    public List<Map<String, String>> getProductionLineList() {
+        return workOrderRoutingMapper.selectProductionLineList();
+    }
+
+    @Override
+    public List<Map<String, String>> getSupplierList() {
+        return workOrderRoutingMapper.selectSupplierList();
+    }
+
+    @Override
+    public Map<String, String> getWorkOrderInfo(String workOrd) {
+        return workOrderRoutingMapper.selectWorkOrderInfo(workOrd);
+    }
+}

+ 13 - 0
yudao-module-makeplan/src/main/java/cn/iocoder/yudao/module/makeplan/service/impl/WorkOrderScheduleServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.makeplan.service.impl;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
 import cn.iocoder.yudao.module.makeplan.controller.admin.workorder.vo.*;
 import cn.iocoder.yudao.module.makeplan.dal.dataobject.WorkOrderScheduleDO;
@@ -8,6 +9,9 @@ import cn.iocoder.yudao.module.makeplan.dal.mysql.WorkOrderScheduleMapper;
 import cn.iocoder.yudao.module.makeplan.service.WorkOrderScheduleService;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mzt.logapi.context.LogRecordContext;
+import com.mzt.logapi.service.impl.DiffParseFunction;
+import com.mzt.logapi.starter.annotation.LogRecord;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.HttpEntity;
@@ -23,6 +27,7 @@ import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
+import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
 
 /**
  * 工单排产 Service 实现类
@@ -72,6 +77,8 @@ public class WorkOrderScheduleServiceImpl implements WorkOrderScheduleService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    @LogRecord(type = WORK_ORDER_SCHEDULE_TYPE, subType = WORK_ORDER_SCHEDULE_UPDATE_SUB_TYPE, 
+            bizNo = "{{#updateReqVO.id}}", success = WORK_ORDER_SCHEDULE_UPDATE_SUCCESS)
     public void updateWorkOrderSchedule(WorkOrderScheduleUpdateReqVO updateReqVO) {
         // 查询工单是否存在
         WorkOrderScheduleDO workOrder = workOrderScheduleMapper.selectById(updateReqVO.getId());
@@ -79,6 +86,12 @@ public class WorkOrderScheduleServiceImpl implements WorkOrderScheduleService {
             throw exception(BAD_REQUEST, "工单不存在");
         }
 
+        // 记录操作日志上下文 - 用于DIFF对比
+        WorkOrderScheduleUpdateReqVO oldData = BeanUtils.toBean(workOrder, WorkOrderScheduleUpdateReqVO.class);
+        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, oldData);
+        LogRecordContext.putVariable("updateReqVO", updateReqVO);
+        LogRecordContext.putVariable("workOrder", workOrder);
+
         // 更新工单信息
         WorkOrderScheduleDO updateDO = new WorkOrderScheduleDO();
         updateDO.setRecId(updateReqVO.getId());

+ 190 - 0
yudao-module-makeplan/src/main/resources/mapper/workorder/WorkOrderRoutingMapper.xml

@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.iocoder.yudao.module.makeplan.dal.mysql.workorder.WorkOrderRoutingMapper">
+
+    <!-- 分页查询工单工序明细(多表关联) -->
+    <select id="selectPageWithJoin" resultType="cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderRoutingDO">
+        SELECT 
+            m.WorkOrd,
+            m.Batch,
+            r.OP,
+            r.Descr as OpName,
+            i.ItemNum,
+            i.Descr as ItemName,
+            r.DueDate,
+            r.MilestoneOp,
+            r.QtyComplete,
+            r.QtyOrded,
+            r.StartDate,
+            r.RecID,
+            r.RecID as id,
+            r.ProcessOut,
+            r.Status,
+            IFNULL(NULLIF(r.machbdnrate, 0), p.Rate) as Rate,
+            IFNULL(NULLIF(r.runcrew, 0), p.StandardStaffCount) as StandardStaffCount,
+            IFNULL(ctr2.Descr, ctr.Descr) as Site,
+            p.OpType,
+            IFNULL(NULLIF(r.Machine, ''), p.InternalEquipmentCode) as InternalEquipmentCode,
+            IFNULL(NULLIF(r.toolcode, ''), p.MoldTypeCode) as MoldTypeCode,
+            IFNULL(NULLIF(r.Engineer, ''), p.SkillNo) as SkillNo,
+            IFNULL(NULLIF(r.stdsetuptime, 0), p.SetupTime) as SetupTime,
+            m.CreateTime,
+            m.UpdateTime,
+            IFNULL(rMN.MachineName, MN.MachineName) as MachineName,
+            m.eff,
+            m.LotSerial
+        FROM WorkOrdRouting r
+        INNER JOIN WorkOrdMaster m ON r.WorkOrdMasterRecID = m.RecID
+        INNER JOIN ItemMaster i ON r.ItemNum = i.ItemNum
+        LEFT JOIN ProdLineDetail p ON r.ItemNum = p.Part AND r.op = p.op AND p.IsActive = 1
+        LEFT JOIN WorkCtrMaster ctr ON p.Site = ctr.WorkCtr
+        LEFT JOIN WorkCtrMaster ctr2 ON r.workctr = ctr2.WorkCtr
+        LEFT JOIN (
+            SELECT a.RecID, GROUP_CONCAT(b.MachineName ORDER BY b.MachineName SEPARATOR '/') AS MachineName
+            FROM (
+                SELECT RecID, SUBSTRING_INDEX(SUBSTRING_INDEX(REPLACE(REPLACE(InternalEquipmentCode, '+', '/'), '/', '/'), '/', numbers.n), '/', -1) AS Code
+                FROM ProdLineDetail
+                CROSS JOIN (
+                    SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
+                    UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
+                ) numbers
+                WHERE CHAR_LENGTH(InternalEquipmentCode) - CHAR_LENGTH(REPLACE(REPLACE(InternalEquipmentCode, '+', ''), '/', '')) >= numbers.n - 1
+                AND LENGTH(SUBSTRING_INDEX(SUBSTRING_INDEX(REPLACE(REPLACE(InternalEquipmentCode, '+', '/'), '/', '/'), '/', numbers.n), '/', -1)) > 0
+            ) a
+            INNER JOIN EquipmentList b ON a.Code = b.InternalEquipmentCode
+            GROUP BY a.RecID
+        ) MN ON p.RecID = MN.RecID
+        LEFT JOIN (
+            SELECT a.RecID, GROUP_CONCAT(b.MachineName ORDER BY b.MachineName SEPARATOR '/') AS MachineName
+            FROM (
+                SELECT RecID, SUBSTRING_INDEX(SUBSTRING_INDEX(REPLACE(REPLACE(machine, '+', '/'), '/', '/'), '/', numbers.n), '/', -1) AS Code
+                FROM WorkOrdRouting
+                CROSS JOIN (
+                    SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
+                    UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10
+                ) numbers
+                WHERE machine IS NOT NULL 
+                AND machine != ''
+                AND CHAR_LENGTH(machine) - CHAR_LENGTH(REPLACE(REPLACE(machine, '+', ''), '/', '')) >= numbers.n - 1
+                AND LENGTH(SUBSTRING_INDEX(SUBSTRING_INDEX(REPLACE(REPLACE(machine, '+', '/'), '/', '/'), '/', numbers.n), '/', -1)) > 0
+            ) a
+            INNER JOIN EquipmentList b ON a.Code = b.InternalEquipmentCode
+            GROUP BY a.RecID
+        ) rMN ON r.recid = rMN.RecID
+        WHERE r.isactive = 1
+        <if test="reqVO.workOrd != null and reqVO.workOrd != ''">
+            AND m.WorkOrd LIKE CONCAT('%', #{reqVO.workOrd}, '%')
+        </if>
+        <if test="reqVO.lotSerial != null and reqVO.lotSerial != ''">
+            AND m.LotSerial LIKE CONCAT('%', #{reqVO.lotSerial}, '%')
+        </if>
+        <if test="reqVO.itemNum != null and reqVO.itemNum != ''">
+            AND i.ItemNum LIKE CONCAT('%', #{reqVO.itemNum}, '%')
+        </if>
+        <if test="reqVO.site != null and reqVO.site != ''">
+            AND (ctr.Descr LIKE CONCAT('%', #{reqVO.site}, '%') OR ctr2.Descr LIKE CONCAT('%', #{reqVO.site}, '%'))
+        </if>
+        <if test="reqVO.createTimeStart != null">
+            AND m.CreateTime &gt;= #{reqVO.createTimeStart}
+        </if>
+        <if test="reqVO.updateTimeStart != null">
+            AND m.UpdateTime &gt;= #{reqVO.updateTimeStart}
+        </if>
+        ORDER BY m.CreateTime DESC
+    </select>
+
+    <!-- 根据ID查询(忽略租户) -->
+    <select id="selectByIdIgnoreTenant" resultType="cn.iocoder.yudao.module.makeplan.dal.dataobject.workorder.WorkOrderRoutingDO">
+        SELECT * FROM WorkOrdRouting WHERE RecID = #{id}
+    </select>
+
+    <!-- 根据ID更新(忽略租户) -->
+    <update id="updateByIdIgnoreTenant">
+        UPDATE WorkOrdRouting
+        <set>
+            <if test="entity.workOrd != null">WorkOrd = #{entity.workOrd},</if>
+            <if test="entity.itemNum != null">ItemNum = #{entity.itemNum},</if>
+            <if test="entity.op != null">OP = #{entity.op},</if>
+            <if test="entity.descr != null">Descr = #{entity.descr},</if>
+            <if test="entity.workCtr != null">WorkCtr = #{entity.workCtr},</if>
+            <if test="entity.prodLine != null">ProdLine = #{entity.prodLine},</if>
+            <if test="entity.stdSetupTime != null">StdSetupTime = #{entity.stdSetupTime},</if>
+            <if test="entity.qtyOrded != null">QtyOrded = #{entity.qtyOrded},</if>
+            <if test="entity.milestoneOp != null">MilestoneOp = #{entity.milestoneOp},</if>
+            <if test="entity.processOut != null">ProcessOut = #{entity.processOut},</if>
+            <if test="entity.processOutSupp != null">ProcessOutSupp = #{entity.processOutSupp},</if>
+            <if test="entity.processOutDay != null">ProcessOutDay = #{entity.processOutDay},</if>
+            <if test="entity.machine != null">Machine = #{entity.machine},</if>
+            <if test="entity.toolCode != null">ToolCode = #{entity.toolCode},</if>
+            <if test="entity.runCrew != null">RunCrew = #{entity.runCrew},</if>
+            <if test="entity.engineer != null">Engineer = #{entity.engineer},</if>
+            <if test="entity.machBdnRate != null">MachBdnRate = #{entity.machBdnRate},</if>
+            <if test="entity.workCode != null">WorkCode = #{entity.workCode},</if>
+            <if test="entity.setup != null">Setup = #{entity.setup},</if>
+            <if test="entity.machinesPerOp != null">MachinesperOp = #{entity.machinesPerOp},</if>
+            <if test="entity.labor != null">Labor = #{entity.labor},</if>
+            <if test="entity.chargeCode != null">ChargeCode = #{entity.chargeCode},</if>
+            <if test="entity.isActive != null">IsActive = #{entity.isActive},</if>
+        </set>
+        WHERE RecID = #{entity.id}
+    </update>
+
+    <!-- 获取工单列表 -->
+    <select id="selectWorkOrderList" resultType="java.util.Map">
+        SELECT workord as value, workord as label
+        FROM WorkOrdMaster
+        WHERE UPPER(IFNULL(Status,'')) IN ('','P','R','W','S')
+        AND UPPER(IFNULL(Typed, '')) IN ('','RW','TEST','TCN')
+        ORDER BY workord
+    </select>
+
+    <!-- 获取工作中心列表 -->
+    <select id="selectWorkCenterList" resultType="java.util.Map">
+        SELECT WorkCtr as value, Descr as label
+        FROM WorkCtrMaster
+        ORDER BY WorkCtr
+    </select>
+
+    <!-- 获取生产线列表 -->
+    <select id="selectProductionLineList" resultType="java.util.Map">
+        SELECT Line as value, `Describe` as label
+        FROM LineMaster
+        ORDER BY Line
+    </select>
+
+    <!-- 获取供应商列表 -->
+    <select id="selectSupplierList" resultType="java.util.Map">
+        SELECT Supp as value, CONCAT(RTRIM(Supp), ' ', IFNULL(SortName, '')) as label
+        FROM SuppMaster
+        ORDER BY Supp
+    </select>
+
+    <!-- 检查工单是否已有相同工序 -->
+    <select id="checkDuplicateOp" resultType="int">
+        SELECT COUNT(*)
+        FROM WorkOrdRouting
+        WHERE WorkOrd = #{workOrd}
+        AND OP = #{op}
+        AND IsActive = 1
+        <if test="excludeId != null">
+            AND RecID != #{excludeId}
+        </if>
+    </select>
+
+    <!-- 检查工序是否有完成数量 -->
+    <select id="checkHasCompleteQty" resultType="int">
+        SELECT COUNT(*)
+        FROM WorkOrdRouting
+        WHERE RecID = #{id}
+        AND IFNULL(QtyComplete, 0) > 0
+    </select>
+
+    <!-- 获取工单信息 -->
+    <select id="selectWorkOrderInfo" resultType="java.util.Map">
+        SELECT ItemNum as itemNum
+        FROM WorkOrdMaster
+        WHERE WorkOrd = #{workOrd}
+        LIMIT 1
+    </select>
+
+</mapper>

+ 26 - 0
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java

@@ -74,4 +74,30 @@ public interface LogRecordConstants {
     String WORK_ORDER_RELEASE_SUB_TYPE = "工单下达";
     String WORK_ORDER_RELEASE_SUCCESS = "下达了工单【{{#workOrder.workOrd}}】,生产批号:{{#releaseReqVO.lotSerial}},开工日期:{{#releaseReqVO.ordDate}}";
 
+    // ======================= WORK_ORDER_MATERIAL 工单物料明细 =======================
+
+    String WORK_ORDER_MATERIAL_TYPE = "MAKEPLAN 工单物料明细";
+    String WORK_ORDER_MATERIAL_CREATE_SUB_TYPE = "创建工单物料明细";
+    String WORK_ORDER_MATERIAL_CREATE_SUCCESS = "创建了工单物料明细,工单:{{#material.workOrd}},物料:{{#material.itemNum}}";
+    String WORK_ORDER_MATERIAL_UPDATE_SUB_TYPE = "更新工单物料明细";
+    String WORK_ORDER_MATERIAL_UPDATE_SUCCESS = "更新了工单物料明细【{{#material.workOrd}}-{{#material.itemNum}}】: {_DIFF{#updateReqVO}}";
+    String WORK_ORDER_MATERIAL_DELETE_SUB_TYPE = "删除工单物料明细";
+    String WORK_ORDER_MATERIAL_DELETE_SUCCESS = "删除了工单物料明细,工单:{{#material.workOrd}},物料:{{#material.itemNum}}";
+
+    // ======================= WORK_ORDER_SCHEDULE 工单排产 =======================
+
+    String WORK_ORDER_SCHEDULE_TYPE = "MAKEPLAN 工单排产";
+    String WORK_ORDER_SCHEDULE_UPDATE_SUB_TYPE = "更新工单排产";
+    String WORK_ORDER_SCHEDULE_UPDATE_SUCCESS = "更新了工单【{{#workOrder.workOrd}}】: {_DIFF{#updateReqVO}}";
+
+    // ======================= WORK_ORDER_ROUTING 工单工序明细 =======================
+
+    String WORK_ORDER_ROUTING_TYPE = "MAKEPLAN 工单工序明细";
+    String WORK_ORDER_ROUTING_CREATE_SUB_TYPE = "创建工单工序明细";
+    String WORK_ORDER_ROUTING_CREATE_SUCCESS = "创建了工单工序明细,工单:{{#routing.workOrd}},工序:{{#routing.op}}";
+    String WORK_ORDER_ROUTING_UPDATE_SUB_TYPE = "更新工单工序明细";
+    String WORK_ORDER_ROUTING_UPDATE_SUCCESS = "更新了工单工序明细【{{#routing.workOrd}}-{{#routing.op}}】: {_DIFF{#updateReqVO}}";
+    String WORK_ORDER_ROUTING_DELETE_SUB_TYPE = "删除工单工序明细";
+    String WORK_ORDER_ROUTING_DELETE_SUCCESS = "删除了工单工序明细,工单:{{#routing.workOrd}},工序:{{#routing.op}}";
+
 }

+ 1 - 1
yudao-ui/yudao-ui-admin-vue3/src/api/jiaohuo/production.ts

@@ -78,7 +78,7 @@ export const checkWorkOrderMaterialExists = (params: any) => {
 
 // 更新工单优先级
 export const updateWorkOrderPriority = (data: any) => {
-  return request.put({ url: '/jiaohuo/production/workorder/priority', data })
+  return request.put({ url: '/makeplan/workorder-schedule/update', data })
 }
 
 

+ 51 - 0
yudao-ui/yudao-ui-admin-vue3/src/api/makeplan/workorderRouting.ts

@@ -0,0 +1,51 @@
+import request from '@/config/axios'
+
+// 获取工单工序明细分页
+export const getWorkOrderRoutingPage = (params: any) => {
+  return request.get({ url: '/makeplan/workorder-routing/page', params })
+}
+
+// 获取工单工序明细详情
+export const getWorkOrderRouting = (id: number) => {
+  return request.get({ url: '/makeplan/workorder-routing/get', params: { id } })
+}
+
+// 创建工单工序明细
+export const createWorkOrderRouting = (data: any) => {
+  return request.post({ url: '/makeplan/workorder-routing/create', data })
+}
+
+// 更新工单工序明细
+export const updateWorkOrderRouting = (data: any) => {
+  return request.put({ url: '/makeplan/workorder-routing/update', data })
+}
+
+// 删除工单工序明细
+export const deleteWorkOrderRouting = (id: number) => {
+  return request.delete({ url: '/makeplan/workorder-routing/delete', params: { id } })
+}
+
+// 获取工单列表(用于下拉选择)
+export const getWorkOrderList = () => {
+  return request.get({ url: '/makeplan/workorder-routing/workorder-list' })
+}
+
+// 获取工作中心列表(用于下拉选择)
+export const getWorkCenterList = () => {
+  return request.get({ url: '/makeplan/workorder-routing/workcenter-list' })
+}
+
+// 获取生产线列表(用于下拉选择)
+export const getProductionLineList = () => {
+  return request.get({ url: '/makeplan/workorder-routing/production-line-list' })
+}
+
+// 获取供应商列表(用于下拉选择)
+export const getSupplierList = () => {
+  return request.get({ url: '/makeplan/workorder-routing/supplier-list' })
+}
+
+// 获取工单信息
+export const getWorkOrderInfo = (workOrd: string) => {
+  return request.get({ url: '/makeplan/workorder-routing/workorder-info', params: { workOrd } })
+}

+ 1 - 5
yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/WorkOrderMaterial.vue

@@ -234,11 +234,7 @@ const handleView = (row) => {
 
 // 获取行样式
 const getRowStyle = ({ row }) => {
-  if (row.isActive === 0) {
-    return {
-      'background-color': '#ffe6e6'
-    }
-  }
+  // 不再为已删除的行设置红色背景
   return {}
 }
 

+ 64 - 48
yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/WorkOrderProcess.vue

@@ -5,7 +5,7 @@
       <el-form :model="searchForm" inline>
         <el-form-item label="生产指令">
           <el-input
-            v-model="searchForm.WorkOrd"
+            v-model="searchForm.workOrd"
             placeholder="请输入生产指令"
             clearable
             style="width: 200px"
@@ -14,7 +14,7 @@
         </el-form-item>
         <el-form-item label="生产批次">
           <el-input
-            v-model="searchForm.LotSerial"
+            v-model="searchForm.lotSerial"
             placeholder="请输入生产批次"
             clearable
             style="width: 200px"
@@ -23,7 +23,7 @@
         </el-form-item>
         <el-form-item label="物料编号">
           <el-input
-            v-model="searchForm.ItemNum"
+            v-model="searchForm.itemNum"
             placeholder="请输入物料编号"
             clearable
             style="width: 200px"
@@ -32,7 +32,7 @@
         </el-form-item>
         <el-form-item label="班组">
           <el-input
-            v-model="searchForm.Site"
+            v-model="searchForm.site"
             placeholder="请输入班组"
             clearable
             style="width: 200px"
@@ -41,7 +41,7 @@
         </el-form-item>
         <el-form-item label="工单创建时间">
           <el-date-picker
-            v-model="searchForm.CreateTime"
+            v-model="searchForm.createTimeStart"
             type="date"
             placeholder="选择日期"
             format="YYYY-MM-DD"
@@ -51,7 +51,7 @@
         </el-form-item>
         <el-form-item label="工单更新时间">
           <el-date-picker
-            v-model="searchForm.UpdateTime"
+            v-model="searchForm.updateTimeStart"
             type="date"
             placeholder="选择日期"
             format="YYYY-MM-DD"
@@ -82,45 +82,53 @@
         style="width: 100%"
       >
         <el-table-column
-          prop="WorkOrd"
+          prop="workOrd"
           label="生产指令"
           min-width="150"
           fixed="left"
         />
-        <el-table-column prop="LotSerial" label="生产批次" width="150" />
+        <el-table-column prop="lotSerial" label="生产批次" width="150" />
         <el-table-column prop="eff" label="下达日期" width="120">
           <template #default="{ row }">
             {{ formatDate(row.eff) }}
           </template>
         </el-table-column>
-        <el-table-column prop="ItemNum" label="物料编号" min-width="150" />
-        <el-table-column prop="ItenName" label="物料名称" min-width="200" />
-        <el-table-column prop="OP" label="工序" width="100" />
-        <el-table-column prop="OpName" label="工序名称" min-width="150" />
-        <el-table-column prop="MilestoneOp" label="关键工序" width="100" align="center" />
-        <el-table-column prop="QtyComplete" label="完成数量" width="120" align="right">
+        <el-table-column prop="itemNum" label="物料编号" min-width="150" />
+        <el-table-column prop="itemName" label="物料名称" min-width="200" />
+        <el-table-column prop="op" label="工序" width="100" />
+        <el-table-column prop="opName" label="工序名称" min-width="150" />
+        <el-table-column prop="milestoneOp" label="关键工序" width="100" align="center">
           <template #default="{ row }">
-            {{ formatNumber(row.QtyComplete) }}
+            {{ row.milestoneOp === 1 ? '是' : '否' }}
           </template>
         </el-table-column>
-        <el-table-column prop="QtyOrded" label="订单数量" width="120" align="right">
+        <el-table-column prop="qtyComplete" label="完成数量" width="120" align="right">
           <template #default="{ row }">
-            {{ formatNumber(row.QtyOrded) }}
+            {{ formatNumber(row.qtyComplete) }}
           </template>
         </el-table-column>
-        <el-table-column prop="ProcessOut" label="工序委外" width="100" align="center" />
-        <el-table-column prop="MachineName" label="设备名称" min-width="150" />
-        <el-table-column prop="StandardStaffCount" label="标准人数" width="100" align="center" />
-        <el-table-column prop="Site" label="班组" width="120" />
-        <el-table-column prop="Rate" label="产能(小时)" width="120" align="right" />
-        <el-table-column prop="CreateTime" label="工单创建时间" width="120">
+        <el-table-column prop="qtyOrded" label="订单数量" width="120" align="right">
           <template #default="{ row }">
-            {{ formatDate(row.CreateTime) }}
+            {{ formatNumber(row.qtyOrded) }}
           </template>
         </el-table-column>
-        <el-table-column prop="UpdateTime" label="工单更新时间" width="120">
+        <el-table-column prop="processOut" label="工序委外" width="100" align="center">
           <template #default="{ row }">
-            {{ formatDate(row.UpdateTime) }}
+            {{ row.processOut === 1 ? '是' : '否' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="machineName" label="设备名称" min-width="150" />
+        <el-table-column prop="standardStaffCount" label="标准人数" width="100" align="center" />
+        <el-table-column prop="site" label="班组" width="120" />
+        <el-table-column prop="rate" label="产能(小时)" width="120" align="right" />
+        <el-table-column prop="createTime" label="工单创建时间" width="120">
+          <template #default="{ row }">
+            {{ formatDate(row.createTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="updateTime" label="工单更新时间" width="120">
+          <template #default="{ row }">
+            {{ formatDate(row.updateTime) }}
           </template>
         </el-table-column>
         <el-table-column label="操作" width="200" fixed="right" align="center">
@@ -165,7 +173,10 @@
 import { ref, reactive, onMounted } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { Search, Refresh, Plus } from '@element-plus/icons-vue'
-import { getWorkOrderProcessList, deleteWorkOrderProcess } from '@/api/jiaohuo/production'
+import { 
+  getWorkOrderRoutingPage, 
+  deleteWorkOrderRouting 
+} from '@/api/makeplan/workorderRouting'
 import { formatNumber, formatDate } from '@/utils/format'
 import WorkOrderProcessForm from './components/WorkOrderProcessForm.vue'
 import { useRoute } from 'vue-router'
@@ -174,12 +185,12 @@ const route = useRoute()
 
 // 搜索表单
 const searchForm = reactive({
-  WorkOrd: '',
-  LotSerial: '',
-  ItemNum: '',
-  Site: '',
-  CreateTime: '',
-  UpdateTime: ''
+  workOrd: '',
+  lotSerial: '',
+  itemNum: '',
+  site: '',
+  createTimeStart: '',
+  updateTimeStart: ''
 })
 
 // 分页
@@ -203,13 +214,18 @@ const handleSearch = async () => {
   try {
     loading.value = true
     const params = {
-      ...searchForm,
-      page: pagination.page,
+      workOrd: searchForm.workOrd || undefined,
+      lotSerial: searchForm.lotSerial || undefined,
+      itemNum: searchForm.itemNum || undefined,
+      site: searchForm.site || undefined,
+      createTimeStart: searchForm.createTimeStart || undefined,
+      updateTimeStart: searchForm.updateTimeStart || undefined,
+      pageNo: pagination.page,
       pageSize: pagination.pageSize
     }
-    const { data } = await getWorkOrderProcessList(params)
-    tableData.value = data.list || []
-    pagination.total = data.total || 0
+    const res = await getWorkOrderRoutingPage(params)
+    tableData.value = res?.list || []
+    pagination.total = res?.total || 0
   } catch (error) {
     ElMessage.error(error.message || '查询失败')
   } finally {
@@ -219,12 +235,12 @@ const handleSearch = async () => {
 
 // 重置搜索
 const handleReset = () => {
-  searchForm.WorkOrd = ''
-  searchForm.LotSerial = ''
-  searchForm.ItemNum = ''
-  searchForm.Site = ''
-  searchForm.CreateTime = ''
-  searchForm.UpdateTime = ''
+  searchForm.workOrd = ''
+  searchForm.lotSerial = ''
+  searchForm.itemNum = ''
+  searchForm.site = ''
+  searchForm.createTimeStart = ''
+  searchForm.updateTimeStart = ''
   pagination.page = 1
   handleSearch()
 }
@@ -251,7 +267,7 @@ const handleDelete = async (row) => {
       cancelButtonText: '取消',
       type: 'warning'
     })
-    await deleteWorkOrderProcess(row.RecID)
+    await deleteWorkOrderRouting(row.id || row.ID || row.recID || row.RecID)
     ElMessage.success('删除成功')
     handleSearch()
   } catch (error) {
@@ -272,10 +288,10 @@ const handleView = (row) => {
 onMounted(() => {
   // 如果从工单排产页面跳转过来,会带上工单编号
   if (route.query.workOrd) {
-    searchForm.WorkOrd = route.query.workOrd
+    searchForm.workOrd = route.query.workOrd
   }
-  // 注释掉自动查询,等后端API准备好后再启用
-  // handleSearch()
+  // 启用自动查询
+  handleSearch()
 })
 </script>
 

+ 8 - 6
yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/components/PriorityAdjustForm.vue

@@ -60,6 +60,7 @@ const currentRow = ref(null)
 const formRef = ref(null)
 
 const formData = reactive({
+  id: undefined,
   WorkOrd: '',
   ItemNum: '',
   QtyOrded: 0,
@@ -91,6 +92,8 @@ const open = (row) => {
   formData.Priority = row.priority || row.Priority || 0
   formData.LotSerial = row.lotSerial || row.LotSerial || ''
   formData.Urgent = row.urgent || row.Urgent || 0
+  // 保存ID用于更新
+  formData.id = row.id || row.ID || row.recId || row.RecId
 }
 
 // 提交
@@ -100,14 +103,13 @@ const handleSubmit = async () => {
 
     submitLoading.value = true
 
+    // 调用后端更新接口,传递符合 WorkOrderScheduleUpdateReqVO 的参数
     await updateWorkOrderPriority({
-      workOrd: formData.WorkOrd,
-      qtyOrded: formData.QtyOrded,
+      id: formData.id,
       priority: formData.Priority,
-      lotSerial: formData.LotSerial,
-      urgent: formData.Urgent,
-      companyId: '', // 从用户状态获取
-      userAccount: '' // 从用户状态获取
+      qtyOrded: formData.QtyOrded,
+      batch: formData.LotSerial,
+      remark: '' // 如果需要备注字段,可以添加到表单中
     })
 
     ElMessage.success('保存成功')

+ 99 - 77
yudao-ui/yudao-ui-admin-vue3/src/views/jiaohuo/components/WorkOrderProcessForm.vue

@@ -25,22 +25,19 @@
             >
               <el-option
                 v-for="item in workOrderList"
-                :key="item.workord"
-                :label="item.workord"
-                :value="item.workord"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
               />
             </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="物料编号" prop="ItemNum">
-            <el-date-picker
+            <el-input
               v-model="formData.ItemNum"
-              type="date"
-              placeholder="选择日期"
-              format="YYYY-MM-DD"
-              value-format="YYYY-MM-DD"
-              style="width: 100%"
+              placeholder="请输入物料编号"
+              disabled
             />
           </el-form-item>
         </el-col>
@@ -49,12 +46,20 @@
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="工序" prop="OP">
-            <el-input v-model="formData.OP" placeholder="请输入工序" />
+            <el-input
+              v-model="formData.OP"
+              placeholder="请输入工序"
+              :disabled="props.mode === 'edit'"
+            />
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="工序名称" prop="Descr">
-            <el-input v-model="formData.Descr" placeholder="请输入工序名称" />
+            <el-input
+              v-model="formData.Descr"
+              placeholder="请输入工序名称"
+              :disabled="props.mode === 'edit'"
+            />
           </el-form-item>
         </el-col>
       </el-row>
@@ -70,9 +75,9 @@
             >
               <el-option
                 v-for="item in workCenterList"
-                :key="item.WorkCtr"
-                :label="`${item.WorkCtr} ${item.Descr}`"
-                :value="item.WorkCtr"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
               />
             </el-select>
           </el-form-item>
@@ -87,9 +92,9 @@
             >
               <el-option
                 v-for="item in productionLineList"
-                :key="item.Line"
-                :label="`${item.Line} ${item.Describe}`"
-                :value="item.Line"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
               />
             </el-select>
           </el-form-item>
@@ -151,9 +156,9 @@
             >
               <el-option
                 v-for="item in supplierList"
-                :key="item.Supp"
-                :label="item.SuppName"
-                :value="item.Supp"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
               />
             </el-select>
           </el-form-item>
@@ -290,15 +295,15 @@
 import { ref, reactive, computed, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import {
+  getWorkOrderRoutingPage,
+  createWorkOrderRouting,
+  updateWorkOrderRouting,
   getWorkOrderList,
   getWorkCenterList,
   getProductionLineList,
   getSupplierList,
-  addWorkOrderProcess,
-  updateWorkOrderProcess,
-  checkWorkOrderProcessExists,
-  checkProcessHasCompleteQty
-} from '@/api/jiaohuo/production'
+  getWorkOrderInfo
+} from '@/api/makeplan/workorderRouting'
 
 const props = defineProps({
   visible: {
@@ -410,29 +415,29 @@ watch(
   (newData) => {
     if (newData) {
       Object.assign(formData, {
-        RecID: newData.RecID || '',
-        WorkOrd: newData.WorkOrd || '',
-        ItemNum: newData.ItemNum || '',
-        OP: newData.OP || '',
-        Descr: newData.Descr || '',
-        WorkCtr: newData.WorkCtr || '',
-        ProdLine: newData.ProdLine || '',
-        StdSetupTime: newData.StdSetupTime || 0,
-        QtyOrded: newData.QtyOrded || 0,
-        MilestoneOp: newData.MilestoneOp === '是' ? true : false,
-        ProcessOut: newData.ProcessOut === '是' ? 1 : 0,
-        ProcessOutSupp: newData.ProcessOutSupp || '',
-        ProcessOutDay: newData.ProcessOutDay || 0,
-        Machine: newData.Machine || '',
-        ToolCode: newData.ToolCode || '',
-        RunCrew: newData.RunCrew || 0,
-        Engineer: newData.Engineer || '',
-        MachBdnRate: newData.MachBdnRate || 0,
-        WorkCode: newData.WorkCode || 'M',
-        Setup: newData.Setup || 0,
-        MachinesperOp: newData.MachinesperOp || 0,
-        Labor: newData.Labor || 0,
-        ChargeCode: newData.ChargeCode || ''
+        RecID: newData.id || newData.RecID || newData.recID || '',
+        WorkOrd: newData.workOrd || newData.WorkOrd || '',
+        ItemNum: newData.itemNum || newData.ItemNum || '',
+        OP: newData.op || newData.OP || '',
+        Descr: newData.descr || newData.Descr || newData.opName || '',
+        WorkCtr: newData.workCtr || newData.WorkCtr || '',
+        ProdLine: newData.prodLine || newData.ProdLine || '',
+        StdSetupTime: newData.stdSetupTime || newData.StdSetupTime || 0,
+        QtyOrded: newData.qtyOrded || newData.QtyOrded || 0,
+        MilestoneOp: (newData.milestoneOp === 1 || newData.MilestoneOp === 1) ? true : false,
+        ProcessOut: (newData.processOut === 1 || newData.ProcessOut === 1) ? 1 : 0,
+        ProcessOutSupp: newData.processOutSupp || newData.ProcessOutSupp || '',
+        ProcessOutDay: newData.processOutDay || newData.ProcessOutDay || 0,
+        Machine: newData.machine || newData.Machine || '',
+        ToolCode: newData.toolCode || newData.ToolCode || '',
+        RunCrew: newData.runCrew || newData.RunCrew || 0,
+        Engineer: newData.engineer || newData.Engineer || '',
+        MachBdnRate: newData.machBdnRate || newData.MachBdnRate || 0,
+        WorkCode: newData.workCode || newData.WorkCode || 'M',
+        Setup: newData.setup || newData.Setup || 0,
+        MachinesperOp: newData.machinesPerOp || newData.MachinesperOp || 0,
+        Labor: newData.labor || newData.Labor || 0,
+        ChargeCode: newData.chargeCode || newData.ChargeCode || ''
       })
     } else {
       resetForm()
@@ -448,6 +453,21 @@ watch(dialogVisible, (val) => {
   }
 })
 
+// 监听工单选择变化 - 暂时注释以排查构建问题
+// watch(() => formData.WorkOrd, async (newWorkOrd, oldWorkOrd) => {
+//   // 只在添加模式且工单真正改变时才执行
+//   if (newWorkOrd && newWorkOrd !== oldWorkOrd && props.mode === 'add') {
+//     try {
+//       const res = await getWorkOrderInfo(newWorkOrd)
+//       if (res && res.itemNum) {
+//         formData.ItemNum = res.itemNum
+//       }
+//     } catch (error) {
+//       console.error('获取工单信息失败:', error)
+//     }
+//   }
+// })
+
 // 加载下拉框数据
 const loadDropdownData = async () => {
   try {
@@ -457,10 +477,10 @@ const loadDropdownData = async () => {
       getProductionLineList(),
       getSupplierList()
     ])
-    workOrderList.value = workOrders.data || []
-    workCenterList.value = workCenters.data || []
-    productionLineList.value = prodLines.data || []
-    supplierList.value = suppliers.data || []
+    workOrderList.value = workOrders || []
+    workCenterList.value = workCenters || []
+    productionLineList.value = prodLines || []
+    supplierList.value = suppliers || []
   } catch (error) {
     console.error('加载下拉框数据失败:', error)
   }
@@ -473,36 +493,38 @@ const handleSubmit = async () => {
 
     submitLoading.value = true
 
-    // 检查工单中是否已有相同工序编号
-    const processExistsRes = await checkWorkOrderProcessExists({
-      WorkOrd: formData.WorkOrd,
-      OP: formData.OP,
-      RecID: formData.RecID
-    })
-    if (processExistsRes.data.exists) {
-      ElMessage.error('该工单中已存在相同工序编号,无法保存')
-      submitLoading.value = false
-      return
-    }
-
-    // 检查后序是否已有完成数量
-    const hasCompleteQtyRes = await checkProcessHasCompleteQty({
-      WorkOrd: formData.WorkOrd,
-      OP: formData.OP
-    })
-    if (hasCompleteQtyRes.data.hasCompleteQty) {
-      ElMessage.error('该工单后序已有完成数量,无法保存')
-      submitLoading.value = false
-      return
+    // 保存数据
+    const saveData = {
+      id: formData.RecID || undefined,
+      workOrd: formData.WorkOrd,
+      itemNum: formData.ItemNum,
+      op: formData.OP,
+      descr: formData.Descr,
+      workCtr: formData.WorkCtr,
+      prodLine: formData.ProdLine,
+      stdSetupTime: formData.StdSetupTime,
+      qtyOrded: formData.QtyOrded,
+      milestoneOp: formData.MilestoneOp ? 1 : 0,
+      processOut: formData.ProcessOut,
+      processOutSupp: formData.ProcessOutSupp,
+      processOutDay: formData.ProcessOutDay,
+      machine: formData.Machine,
+      toolCode: formData.ToolCode,
+      runCrew: formData.RunCrew,
+      engineer: formData.Engineer,
+      machBdnRate: formData.MachBdnRate,
+      workCode: formData.WorkCode,
+      setup: formData.Setup,
+      machinesPerOp: formData.MachinesperOp,
+      labor: formData.Labor,
+      chargeCode: formData.ChargeCode
     }
 
-    // 保存数据
-    const saveData = { ...formData }
     if (props.mode === 'add') {
-      await addWorkOrderProcess(saveData)
+      await createWorkOrderRouting(saveData)
       ElMessage.success('添加成功')
     } else {
-      await updateWorkOrderProcess(formData.RecID, saveData)
+      await updateWorkOrderRouting(saveData)
       ElMessage.success('更新成功')
     }
 

+ 1 - 7
yudao-ui/yudao-ui-admin-vue3/vite.config.ts

@@ -63,16 +63,10 @@ export default ({command, mode}: ConfigEnv): UserConfig => {
             ]
         },
         build: {
-            minify: 'terser',
+            minify: 'esbuild', // 使用 esbuild 代替 terser,更快且内存占用更少
             outDir: env.VITE_OUT_DIR || 'dist',
             sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
             // brotliSize: false,
-            terserOptions: {
-                compress: {
-                    drop_debugger: env.VITE_DROP_DEBUGGER === 'true',
-                    drop_console: env.VITE_DROP_CONSOLE === 'true'
-                }
-            },
             rollupOptions: {
                 output: {
                     manualChunks: {