From 9db84336bc2ba470e08b1b2f3c3ae4c1cf7fbbae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E6=98=8A?= Date: Tue, 2 Jun 2026 18:44:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(bid):=20=E6=96=B0=E5=A2=9E=E5=9F=BA?= =?UTF-8?q?=E4=BA=8E=E7=94=B2=E6=96=B9=E6=8A=A5=E4=BB=B7=E5=BF=AB=E9=80=9F?= =?UTF-8?q?=E5=88=9B=E5=BB=BARFQ=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本次提交完成以下核心变更: 1. 新增RFQ编号自动生成逻辑,添加selectNextRfqNo方法获取月度递增的RFQ编号 2. 在biz_rfq表新增client_quote_id关联字段,添加索引并完善实体类映射 3. 实现基于甲方报价复制物料快速创建RFQ的业务逻辑,包括事务处理和明细复制 4. 新增RFQ列表页关联甲方报价展示,支持点击跳转查看甲方报价详情 5. 在RFQ编辑页新增甲方报价选择器,选中后自动填充对应物料和标题 6. 优化甲方报价单页面,新增生成RFQ按钮和已生成RFQ列表展示 7. 调整RFQ详情页,新增编辑模式支持草稿状态修改 8. 修复路由跳转路径,统一RFQ相关页面路由到/bid/rfq路径组 --- .../web/controller/bid/BizRfqController.java | 25 ++ .../com/ruoyi/system/domain/bid/BizRfq.java | 9 + .../ruoyi/system/mapper/bid/BizRfqMapper.java | 1 + .../system/service/bid/IBizRfqService.java | 2 + .../service/bid/impl/BizRfqServiceImpl.java | 55 ++++- .../resources/mapper/bid/BizRfqMapper.xml | 40 ++- ruoyi-ui/src/api/bid/rfq.js | 4 + ruoyi-ui/src/views/bid/clientquote/index.vue | 98 +++++--- ruoyi-ui/src/views/bid/comparison/index.vue | 2 +- ruoyi-ui/src/views/bid/rfq/detail.vue | 227 ++++++++++++++++-- ruoyi-ui/src/views/bid/rfq/index.vue | 109 ++++++++- sql/20260601/005_rfq_add_client_quote_id.sql | 12 + 12 files changed, 514 insertions(+), 70 deletions(-) create mode 100644 sql/20260601/005_rfq_add_client_quote_id.sql diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizRfqController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizRfqController.java index 5546cd52..ad28ea8a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizRfqController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizRfqController.java @@ -9,14 +9,17 @@ import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.domain.bid.BizClientQuote; import com.ruoyi.system.domain.bid.BizRfq; import com.ruoyi.system.domain.bid.BizRfqItem; +import com.ruoyi.system.mapper.bid.BizClientQuoteMapper; import com.ruoyi.system.service.bid.IBizRfqService; @RestController @RequestMapping("/bid/rfq") public class BizRfqController extends BaseController { @Autowired private IBizRfqService service; + @Autowired private BizClientQuoteMapper clientQuoteMapper; @PreAuthorize("@ss.hasPermi('bid:rfq:list')") @GetMapping("/list") @@ -42,6 +45,8 @@ public class BizRfqController extends BaseController { @PostMapping public AjaxResult add(@RequestBody BizRfq rfq) { rfq.setCreateBy(getUsername()); + Long tenantId = getDeptId(); + rfq.setTenantId(tenantId != null ? tenantId : 1L); return toAjax(service.insertBizRfq(rfq)); } @@ -66,4 +71,24 @@ public class BizRfqController extends BaseController { public AjaxResult remove(@PathVariable Long[] rfqIds) { return toAjax(service.deleteBizRfqByIds(rfqIds)); } + + // ========== 关联甲方报价 ========== + + /** 获取可选甲方报价列表(下拉选择器用) */ + @PreAuthorize("@ss.hasPermi('bid:rfq:add')") + @GetMapping("/client-quote-options") + public AjaxResult clientQuoteOptions() { + BizClientQuote query = new BizClientQuote(); + List list = clientQuoteMapper.selectClientQuoteList(query); + return success(list); + } + + /** 基于甲方报价快速创建 RFQ */ + @PreAuthorize("@ss.hasPermi('bid:rfq:add')") + @Log(title = "报价请求", businessType = BusinessType.INSERT) + @PostMapping("/create-from-quote/{clientQuoteId}") + public AjaxResult createFromQuote(@PathVariable Long clientQuoteId) { + Long tenantId = getDeptId(); + return success(service.createRfqFromClientQuote(clientQuoteId, tenantId != null ? tenantId : 1L)); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizRfq.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizRfq.java index 4bb6d8f2..d3416249 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizRfq.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizRfq.java @@ -13,6 +13,9 @@ public class BizRfq extends BaseEntity { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date deadline; private String deliveryAddr; + private Long clientQuoteId; + private String clientQuoteNo; + private String clientName; private String status; private List items; private List supplierIds; @@ -29,6 +32,12 @@ public class BizRfq extends BaseEntity { public void setDeadline(Date deadline) { this.deadline = deadline; } public String getDeliveryAddr() { return deliveryAddr; } public void setDeliveryAddr(String deliveryAddr) { this.deliveryAddr = deliveryAddr; } + public Long getClientQuoteId() { return clientQuoteId; } + public void setClientQuoteId(Long clientQuoteId) { this.clientQuoteId = clientQuoteId; } + public String getClientQuoteNo() { return clientQuoteNo; } + public void setClientQuoteNo(String clientQuoteNo) { this.clientQuoteNo = clientQuoteNo; } + public String getClientName() { return clientName; } + public void setClientName(String clientName) { this.clientName = clientName; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public List getItems() { return items; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizRfqMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizRfqMapper.java index 4d648faf..3f134176 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizRfqMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizRfqMapper.java @@ -9,5 +9,6 @@ public interface BizRfqMapper { int insertBizRfq(BizRfq record); int updateBizRfq(BizRfq record); int deleteBizRfqById(Long id); + String selectNextRfqNo(); int deleteBizRfqByIds(Long[] ids); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizRfqService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizRfqService.java index 59c7a134..d442cbfa 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizRfqService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizRfqService.java @@ -13,4 +13,6 @@ public interface IBizRfqService { int deleteBizRfqByIds(Long[] ids); int publishRfq(Long rfqId, Long[] supplierIds); List selectItemsByRfqId(Long rfqId); + /** 基于甲方报价快速创建 RFQ(复制物料明细) */ + BizRfq createRfqFromClientQuote(Long clientQuoteId, Long tenantId); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizRfqServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizRfqServiceImpl.java index cf5b4d9b..2614e773 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizRfqServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizRfqServiceImpl.java @@ -1,20 +1,24 @@ package com.ruoyi.system.service.bid.impl; +import com.ruoyi.system.domain.bid.BizClientQuote; +import com.ruoyi.system.domain.bid.BizClientQuoteItem; import com.ruoyi.system.domain.bid.BizRfq; import com.ruoyi.system.domain.bid.BizRfqItem; +import com.ruoyi.system.mapper.bid.BizClientQuoteMapper; import com.ruoyi.system.mapper.bid.BizRfqItemMapper; import com.ruoyi.system.mapper.bid.BizRfqMapper; import com.ruoyi.system.service.bid.IBizRfqService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Date; import java.util.List; +import java.util.stream.Collectors; @Service public class BizRfqServiceImpl implements IBizRfqService { @Autowired private BizRfqMapper rfqMapper; @Autowired private BizRfqItemMapper itemMapper; + @Autowired private BizClientQuoteMapper clientQuoteMapper; @Override public List selectBizRfqList(BizRfq query) { return rfqMapper.selectBizRfqList(query); } @@ -29,6 +33,9 @@ public class BizRfqServiceImpl implements IBizRfqService { @Override @Transactional public int insertBizRfq(BizRfq rfq) { + if (rfq.getRfqNo() == null || rfq.getRfqNo().isBlank()) { + rfq.setRfqNo(rfqMapper.selectNextRfqNo()); + } rfq.setStatus("draft"); int rows = rfqMapper.insertBizRfq(rfq); if (rfq.getItems() != null) { @@ -71,4 +78,50 @@ public class BizRfqServiceImpl implements IBizRfqService { public List selectItemsByRfqId(Long rfqId) { return itemMapper.selectItemsByRfqId(rfqId); } + + @Override + @Transactional + public BizRfq createRfqFromClientQuote(Long clientQuoteId, Long tenantId) { + // 加载甲方报价 + BizClientQuote quote = clientQuoteMapper.selectClientQuoteById(clientQuoteId); + if (quote == null) { + throw new RuntimeException("甲方报价单不存在: " + clientQuoteId); + } + + // 创建 RFQ + BizRfq rfq = new BizRfq(); + rfq.setRfqNo(rfqMapper.selectNextRfqNo()); + rfq.setTenantId(tenantId); + rfq.setRfqTitle("采购需求: " + quote.getClientName() + " - " + quote.getQuoteNo()); + rfq.setClientQuoteId(quote.getQuoteId()); + rfq.setDeliveryAddr(""); + rfq.setStatus("draft"); + rfq.setRemark("源自甲方报价单: " + quote.getQuoteNo()); + + // 复制物料明细 + List sourceItems = clientQuoteMapper.selectItemsByQuoteId(clientQuoteId); + if (sourceItems != null && !sourceItems.isEmpty()) { + rfq.setItems(sourceItems.stream().map(src -> { + BizRfqItem item = new BizRfqItem(); + item.setMaterialId(src.getMaterialId()); + item.setMaterialName(src.getMaterialName()); + item.setSpec(src.getSpec()); + item.setUnit(src.getUnit()); + item.setQuantity(src.getQuantity()); + item.setExpectedPrice(src.getUnitPrice()); + item.setRemark(src.getRemark()); + return item; + }).collect(Collectors.toList())); + } + + rfqMapper.insertBizRfq(rfq); + if (rfq.getItems() != null) { + for (BizRfqItem item : rfq.getItems()) { + item.setRfqId(rfq.getRfqId()); + itemMapper.insertBizRfqItem(item); + } + } + + return rfqMapper.selectBizRfqById(rfq.getRfqId()); + } } diff --git a/ruoyi-system/src/main/resources/mapper/bid/BizRfqMapper.xml b/ruoyi-system/src/main/resources/mapper/bid/BizRfqMapper.xml index c55f8ee8..a911a61f 100644 --- a/ruoyi-system/src/main/resources/mapper/bid/BizRfqMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/bid/BizRfqMapper.xml @@ -9,6 +9,9 @@ + + + @@ -17,22 +20,40 @@ + + SELECT r.*, + cq.quote_no AS clientQuoteNo, + cq.client_name AS clientName + FROM biz_rfq r + LEFT JOIN biz_client_quote cq ON r.client_quote_id = cq.quote_id + + - + + + - INSERT INTO biz_rfq(tenant_id,rfq_no,rfq_title,deadline,delivery_addr,status,remark,create_by,create_time) - VALUES(#{tenantId},#{rfqNo},#{rfqTitle},#{deadline},#{deliveryAddr},#{status},#{remark},#{createBy},NOW()) + INSERT INTO biz_rfq(tenant_id,rfq_no,rfq_title,deadline,delivery_addr,client_quote_id,status,remark,create_by,create_time) + VALUES(#{tenantId},#{rfqNo},#{rfqTitle},#{deadline},#{deliveryAddr},#{clientQuoteId},#{status},#{remark},#{createBy},NOW()) @@ -41,6 +62,7 @@ rfq_title=#{rfqTitle}, deadline=#{deadline}, delivery_addr=#{deliveryAddr}, + client_quote_id=#{clientQuoteId}, status=#{status}, remark=#{remark}, update_by=#{updateBy}, update_time=NOW() diff --git a/ruoyi-ui/src/api/bid/rfq.js b/ruoyi-ui/src/api/bid/rfq.js index 53cf170a..f0de4793 100644 --- a/ruoyi-ui/src/api/bid/rfq.js +++ b/ruoyi-ui/src/api/bid/rfq.js @@ -7,3 +7,7 @@ export const addRfq = (data) => request({ url: baseUrl, method: 'post', data }) export const updateRfq = (data) => request({ url: baseUrl, method: 'put', data }) export const publishRfq = (rfqId, supplierIds) => request({ url: baseUrl + '/publish/' + rfqId, method: 'put', data: supplierIds }) export const delRfq = (ids) => request({ url: baseUrl + '/' + ids, method: 'delete' }) + +// 关联甲方报价 +export const getClientQuoteOptions = () => request({ url: baseUrl + '/client-quote-options', method: 'get' }) +export const createRfqFromQuote = (clientQuoteId) => request({ url: baseUrl + '/create-from-quote/' + clientQuoteId, method: 'post' }) diff --git a/ruoyi-ui/src/views/bid/clientquote/index.vue b/ruoyi-ui/src/views/bid/clientquote/index.vue index 0bb6463e..893c1458 100644 --- a/ruoyi-ui/src/views/bid/clientquote/index.vue +++ b/ruoyi-ui/src/views/bid/clientquote/index.vue @@ -78,12 +78,6 @@ - - -