diff --git a/docs/klp_purchase_requisition_ddl.sql b/docs/klp_purchase_requisition_ddl.sql
new file mode 100644
index 000000000..f9c88bf60
--- /dev/null
+++ b/docs/klp_purchase_requisition_ddl.sql
@@ -0,0 +1,83 @@
+-- ========================================
+-- 电子请购单 — 数据库建表脚本
+-- 表结构:主表 + 明细表,全部文本字段,不关联任何现有表
+-- 对应文档:docs/科仑普请购采购单(1).docx
+-- ========================================
+
+-- ----------------------------
+-- 主表:请购及采购单
+-- ----------------------------
+DROP TABLE IF EXISTS `erp_purchase_requisition`;
+CREATE TABLE `erp_purchase_requisition` (
+ `req_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+
+ -- 头部(请购人填写)
+ `req_dept` VARCHAR(100) DEFAULT NULL COMMENT '请购部门',
+ `item_name` VARCHAR(200) DEFAULT NULL COMMENT '品名',
+ `specification` VARCHAR(200) DEFAULT NULL COMMENT '规格',
+ `unit` VARCHAR(20) DEFAULT NULL COMMENT '单位',
+ `quantity` VARCHAR(50) DEFAULT NULL COMMENT '请购量',
+
+ -- 类别与日期
+ `category` VARCHAR(50) DEFAULT NULL COMMENT '类别',
+ `required_date` VARCHAR(50) DEFAULT NULL COMMENT '需求日期',
+
+ -- 说明
+ `purpose_desc` VARCHAR(500) DEFAULT NULL COMMENT '用途说明',
+ `use_dept` VARCHAR(100) DEFAULT NULL COMMENT '使用部门',
+
+ -- 采购处理(采购单位填写)
+ `inspection_condition` VARCHAR(200) DEFAULT NULL COMMENT '品检条件',
+ `trial_days` VARCHAR(50) DEFAULT NULL COMMENT '试用天数',
+ `prev_purchase_record` VARCHAR(500) DEFAULT NULL COMMENT '前期采购记录',
+ `purchase_proposal` VARCHAR(500) DEFAULT NULL COMMENT '采购拟办',
+ `payment_terms` VARCHAR(200) DEFAULT NULL COMMENT '付款条件',
+ `total_amount` VARCHAR(200) DEFAULT NULL COMMENT '采购金额',
+
+ -- 审批签名(8级)
+ `sign_purchase_chairman` VARCHAR(64) DEFAULT NULL COMMENT '董事长(采购单位)',
+ `sign_purchase_manager` VARCHAR(64) DEFAULT NULL COMMENT '部长/厂长(采购单位)',
+ `sign_purchase_director` VARCHAR(64) DEFAULT NULL COMMENT '主任(采购单位)',
+ `sign_purchase_handler` VARCHAR(64) DEFAULT NULL COMMENT '经办(采购单位)',
+ `sign_request_gm` VARCHAR(64) DEFAULT NULL COMMENT '生产总经理(请购单位)',
+ `sign_request_factory_mgr` VARCHAR(64) DEFAULT NULL COMMENT '厂长(请购单位)',
+ `sign_request_director` VARCHAR(64) DEFAULT NULL COMMENT '主任(请购单位)',
+ `sign_request_handler` VARCHAR(64) DEFAULT NULL COMMENT '经办(请购单位)',
+
+ -- 底部
+ `unload_location` VARCHAR(50) DEFAULT NULL COMMENT '指定卸货位',
+ `unload_other` VARCHAR(200) DEFAULT NULL COMMENT '卸货其他位置',
+
+ -- 状态与控制
+ `form_status` VARCHAR(20) NOT NULL DEFAULT '0' COMMENT '状态:0=请购草稿 1=请购审批中 2=请购已通过 3=采购处理中 4=已完成 5=已驳回',
+ `del_flag` VARCHAR(2) NOT NULL DEFAULT '0' COMMENT '删除标志 0=存在 2=删除',
+ `create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建人',
+ `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
+ `update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
+ `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
+ `remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
+
+ PRIMARY KEY (`req_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='请购及采购单(科仑普)';
+
+
+-- ----------------------------
+-- 明细表:请购及采购单明细(8行)
+-- ----------------------------
+DROP TABLE IF EXISTS `erp_purchase_requisition_item`;
+CREATE TABLE `erp_purchase_requisition_item` (
+ `item_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '明细ID',
+ `req_id` BIGINT NOT NULL COMMENT '关联请购单ID → erp_purchase_requisition.req_id',
+ `seq` VARCHAR(10) NOT NULL COMMENT '项次',
+ `item_name` VARCHAR(200) DEFAULT NULL COMMENT '品名',
+ `specification` VARCHAR(200) DEFAULT NULL COMMENT '规格',
+ `unit` VARCHAR(20) DEFAULT NULL COMMENT '单位',
+ `quantity` VARCHAR(50) DEFAULT NULL COMMENT '请购量',
+ `del_flag` VARCHAR(2) NOT NULL DEFAULT '0' COMMENT '删除标志',
+ `create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建人',
+ `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
+ `update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
+ `update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
+ PRIMARY KEY (`item_id`),
+ KEY `idx_req_id` (`req_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='请购及采购单明细';
diff --git a/docs/rolling_mill_project_management(1).html b/docs/rolling_mill_project_management(1).html
new file mode 100644
index 000000000..eb72cc78f
--- /dev/null
+++ b/docs/rolling_mill_project_management(1).html
@@ -0,0 +1,4645 @@
+
+
+
+
+
+连轧机/可逆轧机设备总包项目管理系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🧠 思维导图编辑器
+
+
+
+
+
+
+
+
+
+
+
+
+ 100%
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/klp-erp/src/main/java/com/klp/erp/controller/ErpPurchaseRequisitionController.java b/klp-erp/src/main/java/com/klp/erp/controller/ErpPurchaseRequisitionController.java
new file mode 100644
index 000000000..9b3d0f05a
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/controller/ErpPurchaseRequisitionController.java
@@ -0,0 +1,82 @@
+package com.klp.erp.controller;
+
+import com.klp.common.annotation.Log;
+import com.klp.common.annotation.RepeatSubmit;
+import com.klp.common.core.controller.BaseController;
+import com.klp.common.core.domain.PageQuery;
+import com.klp.common.core.domain.R;
+import com.klp.common.core.page.TableDataInfo;
+import com.klp.common.core.validate.AddGroup;
+import com.klp.common.core.validate.EditGroup;
+import com.klp.common.enums.BusinessType;
+import com.klp.common.utils.poi.ExcelUtil;
+import com.klp.erp.domain.bo.ErpPurchaseRequisitionBo;
+import com.klp.erp.domain.vo.ErpPurchaseRequisitionVo;
+import com.klp.erp.service.IErpPurchaseRequisitionService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 请购及采购单
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/erp/purchaseRequisition")
+public class ErpPurchaseRequisitionController extends BaseController {
+
+ private final IErpPurchaseRequisitionService iErpPurchaseRequisitionService;
+
+ /** 查询请购单列表 */
+ @GetMapping("/list")
+ public TableDataInfo list(ErpPurchaseRequisitionBo bo, PageQuery pageQuery) {
+ return iErpPurchaseRequisitionService.queryPageList(bo, pageQuery);
+ }
+
+ /** 导出请购单列表 */
+ @Log(title = "请购及采购单", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export(ErpPurchaseRequisitionBo bo, HttpServletResponse response) {
+ List list = iErpPurchaseRequisitionService.queryList(bo);
+ ExcelUtil.exportExcel(list, "请购及采购单", ErpPurchaseRequisitionVo.class, response);
+ }
+
+ /** 获取请购单详细信息(含明细) */
+ @GetMapping("/{reqId}")
+ public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long reqId) {
+ return R.ok(iErpPurchaseRequisitionService.queryById(reqId));
+ }
+
+ /** 新增请购单 */
+ @Log(title = "请购及采购单", businessType = BusinessType.INSERT)
+ @RepeatSubmit()
+ @PostMapping()
+ public R add(@Validated(AddGroup.class) @RequestBody ErpPurchaseRequisitionBo bo) {
+ return toAjax(iErpPurchaseRequisitionService.insertByBo(bo));
+ }
+
+ /** 修改请购单 */
+ @Log(title = "请购及采购单", businessType = BusinessType.UPDATE)
+ @RepeatSubmit()
+ @PutMapping()
+ public R edit(@Validated(EditGroup.class) @RequestBody ErpPurchaseRequisitionBo bo) {
+ return toAjax(iErpPurchaseRequisitionService.updateByBo(bo));
+ }
+
+ /** 删除请购单 */
+ @Log(title = "请购及采购单", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{reqIds}")
+ public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] reqIds) {
+ return toAjax(iErpPurchaseRequisitionService.deleteWithValidByIds(Arrays.asList(reqIds), true));
+ }
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchaseRequisition.java b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchaseRequisition.java
new file mode 100644
index 000000000..16a1782f6
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchaseRequisition.java
@@ -0,0 +1,111 @@
+package com.klp.erp.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.klp.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 请购及采购单对象 erp_purchase_requisition
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("erp_purchase_requisition")
+public class ErpPurchaseRequisition extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /** 主键 */
+ @TableId(value = "req_id")
+ private Long reqId;
+
+ /** 请购部门 */
+ private String reqDept;
+
+ /** 品名 */
+ private String itemName;
+
+ /** 规格 */
+ private String specification;
+
+ /** 单位 */
+ private String unit;
+
+ /** 请购量 */
+ private String quantity;
+
+ /** 类别 */
+ private String category;
+
+ /** 需求日期 */
+ private String requiredDate;
+
+ /** 用途说明 */
+ private String purposeDesc;
+
+ /** 使用部门 */
+ private String useDept;
+
+ /** 品检条件 */
+ private String inspectionCondition;
+
+ /** 试用天数 */
+ private String trialDays;
+
+ /** 前期采购记录 */
+ private String prevPurchaseRecord;
+
+ /** 采购拟办 */
+ private String purchaseProposal;
+
+ /** 付款条件 */
+ private String paymentTerms;
+
+ /** 采购金额 */
+ private String totalAmount;
+
+ /** 董事长(采购单位) */
+ private String signPurchaseChairman;
+
+ /** 部长/厂长(采购单位) */
+ private String signPurchaseManager;
+
+ /** 主任(采购单位) */
+ private String signPurchaseDirector;
+
+ /** 经办(采购单位) */
+ private String signPurchaseHandler;
+
+ /** 生产总经理(请购单位) */
+ private String signRequestGm;
+
+ /** 厂长(请购单位) */
+ private String signRequestFactoryMgr;
+
+ /** 主任(请购单位) */
+ private String signRequestDirector;
+
+ /** 经办(请购单位) */
+ private String signRequestHandler;
+
+ /** 指定卸货位 */
+ private String unloadLocation;
+
+ /** 卸货其他位置 */
+ private String unloadOther;
+
+ /** 状态 */
+ private String formStatus;
+
+ /** 删除标志 */
+ @TableLogic
+ private String delFlag;
+
+ /** 备注 */
+ private String remark;
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchaseRequisitionItem.java b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchaseRequisitionItem.java
new file mode 100644
index 000000000..199c62c82
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/domain/ErpPurchaseRequisitionItem.java
@@ -0,0 +1,48 @@
+package com.klp.erp.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.klp.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 请购及采购单明细对象 erp_purchase_requisition_item
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("erp_purchase_requisition_item")
+public class ErpPurchaseRequisitionItem extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /** 明细ID */
+ @TableId(value = "item_id")
+ private Long itemId;
+
+ /** 关联请购单ID */
+ private Long reqId;
+
+ /** 项次 */
+ private String seq;
+
+ /** 品名 */
+ private String itemName;
+
+ /** 规格 */
+ private String specification;
+
+ /** 单位 */
+ private String unit;
+
+ /** 请购量 */
+ private String quantity;
+
+ /** 删除标志 */
+ @TableLogic
+ private String delFlag;
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpPurchaseRequisitionBo.java b/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpPurchaseRequisitionBo.java
new file mode 100644
index 000000000..a7d12f3dc
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpPurchaseRequisitionBo.java
@@ -0,0 +1,108 @@
+package com.klp.erp.domain.bo;
+
+import com.klp.common.core.domain.BaseEntity;
+import com.klp.common.core.validate.EditGroup;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 请购及采购单业务对象 erp_purchase_requisition
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ErpPurchaseRequisitionBo extends BaseEntity {
+
+ /** 主键 */
+ @NotNull(message = "主键不能为空", groups = {EditGroup.class})
+ private Long reqId;
+
+ /** 请购部门 */
+ private String reqDept;
+
+ /** 品名 */
+ private String itemName;
+
+ /** 规格 */
+ private String specification;
+
+ /** 单位 */
+ private String unit;
+
+ /** 请购量 */
+ private String quantity;
+
+ /** 类别 */
+ private String category;
+
+ /** 需求日期 */
+ private String requiredDate;
+
+ /** 用途说明 */
+ private String purposeDesc;
+
+ /** 使用部门 */
+ private String useDept;
+
+ /** 品检条件 */
+ private String inspectionCondition;
+
+ /** 试用天数 */
+ private String trialDays;
+
+ /** 前期采购记录 */
+ private String prevPurchaseRecord;
+
+ /** 采购拟办 */
+ private String purchaseProposal;
+
+ /** 付款条件 */
+ private String paymentTerms;
+
+ /** 采购金额 */
+ private String totalAmount;
+
+ /** 董事长(采购单位) */
+ private String signPurchaseChairman;
+
+ /** 部长/厂长(采购单位) */
+ private String signPurchaseManager;
+
+ /** 主任(采购单位) */
+ private String signPurchaseDirector;
+
+ /** 经办(采购单位) */
+ private String signPurchaseHandler;
+
+ /** 生产总经理(请购单位) */
+ private String signRequestGm;
+
+ /** 厂长(请购单位) */
+ private String signRequestFactoryMgr;
+
+ /** 主任(请购单位) */
+ private String signRequestDirector;
+
+ /** 经办(请购单位) */
+ private String signRequestHandler;
+
+ /** 指定卸货位 */
+ private String unloadLocation;
+
+ /** 卸货其他位置 */
+ private String unloadOther;
+
+ /** 状态 */
+ private String formStatus;
+
+ /** 备注 */
+ private String remark;
+
+ /** 明细行 */
+ private List items;
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpPurchaseRequisitionItemBo.java b/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpPurchaseRequisitionItemBo.java
new file mode 100644
index 000000000..57e0ede24
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/domain/bo/ErpPurchaseRequisitionItemBo.java
@@ -0,0 +1,37 @@
+package com.klp.erp.domain.bo;
+
+import com.klp.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 请购及采购单明细业务对象 erp_purchase_requisition_item
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ErpPurchaseRequisitionItemBo extends BaseEntity {
+
+ /** 明细ID */
+ private Long itemId;
+
+ /** 关联请购单ID */
+ private Long reqId;
+
+ /** 项次 */
+ private String seq;
+
+ /** 品名 */
+ private String itemName;
+
+ /** 规格 */
+ private String specification;
+
+ /** 单位 */
+ private String unit;
+
+ /** 请购量 */
+ private String quantity;
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchaseRequisitionItemVo.java b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchaseRequisitionItemVo.java
new file mode 100644
index 000000000..5aefa57df
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchaseRequisitionItemVo.java
@@ -0,0 +1,40 @@
+package com.klp.erp.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 请购及采购单明细视图对象 erp_purchase_requisition_item
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class ErpPurchaseRequisitionItemVo implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @ExcelProperty(value = "明细ID")
+ private Long itemId;
+
+ private Long reqId;
+
+ @ExcelProperty(value = "项次")
+ private String seq;
+
+ @ExcelProperty(value = "品名")
+ private String itemName;
+
+ @ExcelProperty(value = "规格")
+ private String specification;
+
+ @ExcelProperty(value = "单位")
+ private String unit;
+
+ @ExcelProperty(value = "请购量")
+ private String quantity;
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchaseRequisitionVo.java b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchaseRequisitionVo.java
new file mode 100644
index 000000000..8a715a776
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/domain/vo/ErpPurchaseRequisitionVo.java
@@ -0,0 +1,108 @@
+package com.klp.erp.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 请购及采购单视图对象 erp_purchase_requisition
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@Data
+@ExcelIgnoreUnannotated
+public class ErpPurchaseRequisitionVo implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @ExcelProperty(value = "主键")
+ private Long reqId;
+
+ @ExcelProperty(value = "请购部门")
+ private String reqDept;
+
+ @ExcelProperty(value = "品名")
+ private String itemName;
+
+ @ExcelProperty(value = "规格")
+ private String specification;
+
+ @ExcelProperty(value = "单位")
+ private String unit;
+
+ @ExcelProperty(value = "请购量")
+ private String quantity;
+
+ @ExcelProperty(value = "类别")
+ private String category;
+
+ @ExcelProperty(value = "需求日期")
+ private String requiredDate;
+
+ @ExcelProperty(value = "用途说明")
+ private String purposeDesc;
+
+ @ExcelProperty(value = "使用部门")
+ private String useDept;
+
+ @ExcelProperty(value = "品检条件")
+ private String inspectionCondition;
+
+ @ExcelProperty(value = "试用天数")
+ private String trialDays;
+
+ @ExcelProperty(value = "前期采购记录")
+ private String prevPurchaseRecord;
+
+ @ExcelProperty(value = "采购拟办")
+ private String purchaseProposal;
+
+ @ExcelProperty(value = "付款条件")
+ private String paymentTerms;
+
+ @ExcelProperty(value = "采购金额")
+ private String totalAmount;
+
+ @ExcelProperty(value = "董事长(采购单位)")
+ private String signPurchaseChairman;
+
+ @ExcelProperty(value = "部长/厂长(采购单位)")
+ private String signPurchaseManager;
+
+ @ExcelProperty(value = "主任(采购单位)")
+ private String signPurchaseDirector;
+
+ @ExcelProperty(value = "经办(采购单位)")
+ private String signPurchaseHandler;
+
+ @ExcelProperty(value = "生产总经理(请购单位)")
+ private String signRequestGm;
+
+ @ExcelProperty(value = "厂长(请购单位)")
+ private String signRequestFactoryMgr;
+
+ @ExcelProperty(value = "主任(请购单位)")
+ private String signRequestDirector;
+
+ @ExcelProperty(value = "经办(请购单位)")
+ private String signRequestHandler;
+
+ @ExcelProperty(value = "指定卸货位")
+ private String unloadLocation;
+
+ @ExcelProperty(value = "卸货其他位置")
+ private String unloadOther;
+
+ @ExcelProperty(value = "状态")
+ private String formStatus;
+
+ @ExcelProperty(value = "备注")
+ private String remark;
+
+ /** 明细行 */
+ private List items;
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchaseRequisitionItemMapper.java b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchaseRequisitionItemMapper.java
new file mode 100644
index 000000000..717c94fbe
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchaseRequisitionItemMapper.java
@@ -0,0 +1,15 @@
+package com.klp.erp.mapper;
+
+import com.klp.common.core.mapper.BaseMapperPlus;
+import com.klp.erp.domain.ErpPurchaseRequisitionItem;
+import com.klp.erp.domain.vo.ErpPurchaseRequisitionItemVo;
+
+/**
+ * 请购及采购单明细Mapper接口
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+public interface ErpPurchaseRequisitionItemMapper extends BaseMapperPlus {
+
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchaseRequisitionMapper.java b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchaseRequisitionMapper.java
new file mode 100644
index 000000000..c70f4efe6
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/mapper/ErpPurchaseRequisitionMapper.java
@@ -0,0 +1,15 @@
+package com.klp.erp.mapper;
+
+import com.klp.common.core.mapper.BaseMapperPlus;
+import com.klp.erp.domain.ErpPurchaseRequisition;
+import com.klp.erp.domain.vo.ErpPurchaseRequisitionVo;
+
+/**
+ * 请购及采购单Mapper接口
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+public interface ErpPurchaseRequisitionMapper extends BaseMapperPlus {
+
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/service/IErpPurchaseRequisitionService.java b/klp-erp/src/main/java/com/klp/erp/service/IErpPurchaseRequisitionService.java
new file mode 100644
index 000000000..131ccc649
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/service/IErpPurchaseRequisitionService.java
@@ -0,0 +1,36 @@
+package com.klp.erp.service;
+
+import com.klp.common.core.domain.PageQuery;
+import com.klp.common.core.page.TableDataInfo;
+import com.klp.erp.domain.bo.ErpPurchaseRequisitionBo;
+import com.klp.erp.domain.vo.ErpPurchaseRequisitionVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 请购及采购单Service接口
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+public interface IErpPurchaseRequisitionService {
+
+ /** 查询请购单详情(含明细) */
+ ErpPurchaseRequisitionVo queryById(Long reqId);
+
+ /** 分页查询请购单 */
+ TableDataInfo queryPageList(ErpPurchaseRequisitionBo bo, PageQuery pageQuery);
+
+ /** 查询请购单列表 */
+ List queryList(ErpPurchaseRequisitionBo bo);
+
+ /** 新增请购单(含明细) */
+ Boolean insertByBo(ErpPurchaseRequisitionBo bo);
+
+ /** 修改请购单(含明细) */
+ Boolean updateByBo(ErpPurchaseRequisitionBo bo);
+
+ /** 校验并批量删除请购单 */
+ Boolean deleteWithValidByIds(Collection ids, Boolean isValid);
+}
diff --git a/klp-erp/src/main/java/com/klp/erp/service/impl/ErpPurchaseRequisitionServiceImpl.java b/klp-erp/src/main/java/com/klp/erp/service/impl/ErpPurchaseRequisitionServiceImpl.java
new file mode 100644
index 000000000..e13ee85ff
--- /dev/null
+++ b/klp-erp/src/main/java/com/klp/erp/service/impl/ErpPurchaseRequisitionServiceImpl.java
@@ -0,0 +1,124 @@
+package com.klp.erp.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.klp.common.core.domain.PageQuery;
+import com.klp.common.core.page.TableDataInfo;
+import com.klp.common.utils.StringUtils;
+import com.klp.erp.domain.ErpPurchaseRequisition;
+import com.klp.erp.domain.ErpPurchaseRequisitionItem;
+import com.klp.erp.domain.bo.ErpPurchaseRequisitionBo;
+import com.klp.erp.domain.bo.ErpPurchaseRequisitionItemBo;
+import com.klp.erp.domain.vo.ErpPurchaseRequisitionVo;
+import com.klp.erp.mapper.ErpPurchaseRequisitionItemMapper;
+import com.klp.erp.mapper.ErpPurchaseRequisitionMapper;
+import com.klp.erp.service.IErpPurchaseRequisitionService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 请购及采购单Service业务层处理
+ *
+ * @author klp
+ * @date 2026-06-29
+ */
+@RequiredArgsConstructor
+@Service
+public class ErpPurchaseRequisitionServiceImpl implements IErpPurchaseRequisitionService {
+
+ private final ErpPurchaseRequisitionMapper baseMapper;
+ private final ErpPurchaseRequisitionItemMapper itemMapper;
+
+ @Override
+ public ErpPurchaseRequisitionVo queryById(Long reqId) {
+ ErpPurchaseRequisitionVo vo = baseMapper.selectVoById(reqId);
+ if (vo == null) {
+ return null;
+ }
+ // 明细
+ vo.setItems(itemMapper.selectVoList(Wrappers.lambdaQuery(ErpPurchaseRequisitionItem.class)
+ .eq(ErpPurchaseRequisitionItem::getReqId, reqId)));
+ return vo;
+ }
+
+ @Override
+ public TableDataInfo queryPageList(ErpPurchaseRequisitionBo bo, PageQuery pageQuery) {
+ Page result = baseMapper.selectVoPage(pageQuery.build(), buildQueryWrapper(bo));
+ return TableDataInfo.build(result);
+ }
+
+ @Override
+ public List queryList(ErpPurchaseRequisitionBo bo) {
+ return baseMapper.selectVoList(buildQueryWrapper(bo));
+ }
+
+ private LambdaQueryWrapper buildQueryWrapper(ErpPurchaseRequisitionBo bo) {
+ LambdaQueryWrapper lqw = Wrappers.lambdaQuery();
+ lqw.like(StringUtils.isNotBlank(bo.getReqDept()), ErpPurchaseRequisition::getReqDept, bo.getReqDept());
+ lqw.like(StringUtils.isNotBlank(bo.getItemName()), ErpPurchaseRequisition::getItemName, bo.getItemName());
+ lqw.eq(StringUtils.isNotBlank(bo.getFormStatus()), ErpPurchaseRequisition::getFormStatus, bo.getFormStatus());
+ lqw.orderByDesc(ErpPurchaseRequisition::getReqId);
+ return lqw;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean insertByBo(ErpPurchaseRequisitionBo bo) {
+ ErpPurchaseRequisition add = BeanUtil.toBean(bo, ErpPurchaseRequisition.class);
+ if (StringUtils.isBlank(add.getFormStatus())) {
+ add.setFormStatus("0");
+ }
+ add.setReqId(null);
+ if (baseMapper.insert(add) <= 0) {
+ return false;
+ }
+ bo.setReqId(add.getReqId());
+ saveItems(add.getReqId(), bo.getItems());
+ return true;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean updateByBo(ErpPurchaseRequisitionBo bo) {
+ ErpPurchaseRequisition update = baseMapper.selectById(bo.getReqId());
+ if (update == null) {
+ return false;
+ }
+ BeanUtil.copyProperties(bo, update, "reqId", "createBy", "createTime");
+ baseMapper.updateById(update);
+ // 覆盖式重写明细
+ itemMapper.delete(Wrappers.lambdaQuery(ErpPurchaseRequisitionItem.class)
+ .eq(ErpPurchaseRequisitionItem::getReqId, bo.getReqId()));
+ saveItems(bo.getReqId(), bo.getItems());
+ return true;
+ }
+
+ private void saveItems(Long reqId, List items) {
+ if (items == null || items.isEmpty()) {
+ return;
+ }
+ for (ErpPurchaseRequisitionItemBo itemBo : items) {
+ ErpPurchaseRequisitionItem item = BeanUtil.toBean(itemBo, ErpPurchaseRequisitionItem.class);
+ item.setItemId(null);
+ item.setReqId(reqId);
+ itemMapper.insert(item);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
+ for (Long reqId : ids) {
+ itemMapper.delete(Wrappers.lambdaQuery(ErpPurchaseRequisitionItem.class)
+ .eq(ErpPurchaseRequisitionItem::getReqId, reqId));
+ }
+ return baseMapper.deleteBatchIds(ids) > 0;
+ }
+
+}
diff --git a/klp-ui/src/api/erp/purchaseRequisition.js b/klp-ui/src/api/erp/purchaseRequisition.js
new file mode 100644
index 000000000..77d9fdbc6
--- /dev/null
+++ b/klp-ui/src/api/erp/purchaseRequisition.js
@@ -0,0 +1,54 @@
+import request from '@/utils/request'
+
+// 查询请购单列表
+export function listPurchaseRequisition(query) {
+ return request({
+ url: '/erp/purchaseRequisition/list',
+ method: 'get',
+ params: query
+ })
+}
+
+// 查询请购单详细(含明细)
+export function getPurchaseRequisition(reqId) {
+ return request({
+ url: '/erp/purchaseRequisition/' + reqId,
+ method: 'get'
+ })
+}
+
+// 新增请购单
+export function addPurchaseRequisition(data) {
+ return request({
+ url: '/erp/purchaseRequisition',
+ method: 'post',
+ data
+ })
+}
+
+// 修改请购单
+export function updatePurchaseRequisition(data) {
+ return request({
+ url: '/erp/purchaseRequisition',
+ method: 'put',
+ data
+ })
+}
+
+// 删除请购单
+export function delPurchaseRequisition(reqIds) {
+ return request({
+ url: '/erp/purchaseRequisition/' + reqIds,
+ method: 'delete'
+ })
+}
+
+// 导出请购单
+export function exportPurchaseRequisition(query) {
+ return request({
+ url: '/erp/purchaseRequisition/export',
+ method: 'post',
+ params: query,
+ responseType: 'blob'
+ })
+}
diff --git a/klp-ui/src/views/erp/purchaseRequisition/index.vue b/klp-ui/src/views/erp/purchaseRequisition/index.vue
new file mode 100644
index 000000000..3fafa54fc
--- /dev/null
+++ b/klp-ui/src/views/erp/purchaseRequisition/index.vue
@@ -0,0 +1,862 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 搜索
+ 重置
+
+
+
+
+
+ 新增
+
+
+ 修改
+
+
+ 删除
+
+
+ 导出
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ statusText(scope.row.formStatus) }}
+
+
+
+
+
+ {{ parseTime(scope.row.createTime) }}
+
+
+
+
+ 查看
+ 修改
+ 删除
+
+
+
+
+
+
+
+
+
+ 请购信息
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 采购处理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 请购明细
+ 加行
+ 共 {{ form.items.length }} 项
+
+
+
+ {{ s.$index + 1 }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 点「加行」添加明细
+
+
+ 审批签名
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
请购信息
+
+ {{ viewForm.reqDept || '—' }}
+ {{ viewForm.itemName || '—' }}
+ {{ viewForm.specification || '—' }}
+
+
+ {{ viewForm.unit || '—' }}
+ {{ viewForm.quantity || '—' }}
+ {{ viewForm.category || '—' }}
+
+
+ {{ viewForm.requiredDate || '—' }}
+ {{ viewForm.useDept || '—' }}
+
+
+ {{ viewForm.purposeDesc || '—' }}
+
+
+
采购处理
+
+ {{ viewForm.inspectionCondition || '—' }}
+ {{ viewForm.trialDays || '—' }}
+ {{ viewForm.paymentTerms || '—' }}
+
+
+ {{ viewForm.prevPurchaseRecord || '—' }}
+ {{ viewForm.totalAmount || '—' }}
+
+
+ {{ viewForm.purchaseProposal || '—' }}
+
+
+
请购明细
+
+
+ {{ s.$index + 1 }}
+
+
+
+
+
+ 无明细
+
+
+
审批签名
+
+ {{ viewForm.signRequestHandler || '—' }}
+ {{ viewForm.signRequestDirector || '—' }}
+ {{ viewForm.signRequestFactoryMgr || '—' }}
+
+
+ {{ viewForm.signRequestGm || '—' }}
+ {{ viewForm.signPurchaseHandler || '—' }}
+ {{ viewForm.signPurchaseDirector || '—' }}
+
+
+ {{ viewForm.signPurchaseManager || '—' }}
+ {{ viewForm.signPurchaseChairman || '—' }}
+ {{ viewForm.unloadLocation || '—' }}
+
+
+ {{ viewForm.unloadOther || '—' }}
+ {{ statusText(viewForm.formStatus) }}
+
+
+ {{ viewForm.remark || '—' }}
+
+
+
+
+
+
+
+
+
+
科仑普重工有限公司
+
请 购 及 采 购 单
+
+
+
+
+ | 请购部门 |
+ {{ printData.reqDept || '' }} |
+ 品 名 |
+ {{ printData.itemName || '' }} |
+ 规 格 |
+ {{ printData.specification || '' }} |
+ 单位 |
+ {{ printData.unit || '' }} |
+ 请购量 |
+ {{ printData.quantity || '' }} |
+ 用途 |
+ {{ printData.category || '' }} |
+
+
+ | 类 别 |
+
+ □原料物料
+ □设备增添
+ □工程劳务
+ □其它用途
+ |
+
+
+ | 需求日期 |
+ {{ printData.requiredDate || '' }} |
+ 用途说明 |
+ {{ printData.purposeDesc || '' }} |
+ 使用部门 |
+ {{ printData.useDept || '' }} |
+
+
+
+
+
明细
+
+
+
+ | 项次 |
+ 品名 |
+ 规格 |
+ 单位 |
+ 请购量 |
+ 项次 |
+ 品名 |
+ 规格 |
+ 单位 |
+ 请购量 |
+
+
+
+
+ | {{ idx }} |
+ {{ printData.items && printData.items[idx-1] ? (printData.items[idx-1].itemName || '') : '' }} |
+ {{ printData.items && printData.items[idx-1] ? (printData.items[idx-1].specification || '') : '' }} |
+ {{ printData.items && printData.items[idx-1] ? (printData.items[idx-1].unit || '') : '' }} |
+ {{ printData.items && printData.items[idx-1] ? (printData.items[idx-1].quantity || '') : '' }} |
+ {{ idx + 4 }} |
+ {{ printData.items && printData.items[idx+3] ? (printData.items[idx+3].itemName || '') : '' }} |
+ {{ printData.items && printData.items[idx+3] ? (printData.items[idx+3].specification || '') : '' }} |
+ {{ printData.items && printData.items[idx+3] ? (printData.items[idx+3].unit || '') : '' }} |
+ {{ printData.items && printData.items[idx+3] ? (printData.items[idx+3].quantity || '') : '' }} |
+
+
+
+
+
+
+
+ | 品检条件 |
+
+ □1.一般规格/目视检验
+ □2.品保检验品
+ □3.工程、劳务/特殊规格品
+ □4.试用
+ {{ printData.trialDays || '____' }} 天后验收
+ |
+
+
+ | 前期采购记录 |
+ {{ printData.prevPurchaseRecord || '' }} |
+
+
+ | 采购拟办 |
+ {{ printData.purchaseProposal || '' }} |
+
+
+ | 付款条件 |
+ {{ printData.paymentTerms || '' }} |
+ 采购金额(人民币) |
+
+ ¥ {{ printData.totalAmount || '' }}
+ |
+
+
+
+
+
+
+ | 采购单位 |
+ 请购单位 |
+
+
+ | 董事长 |
+ 部长/厂长 |
+ 主任 |
+ 经办 |
+ 生产总经理 |
+ 厂长 |
+ 主任 |
+ 经办 |
+
+
+ | {{ printData.signPurchaseChairman || '' }} |
+ {{ printData.signPurchaseManager || '' }} |
+ {{ printData.signPurchaseDirector || '' }} |
+ {{ printData.signPurchaseHandler || '' }} |
+ {{ printData.signRequestGm || '' }} |
+ {{ printData.signRequestFactoryMgr || '' }} |
+ {{ printData.signRequestDirector || '' }} |
+ {{ printData.signRequestHandler || '' }} |
+
+
+
+
+
+
+ ※物品进厂 指定卸货位 |
+ {{ printData.unloadLocation || '' }} |
+ 置于 |
+ {{ printData.unloadOther || '' }} |
+ 状态 |
+ {{ statusText(printData.formStatus) }} |
+
+
+
+
+
+
+
+
+
+