From aa2efbd42e404aecb8a12244446124c8efde748b Mon Sep 17 00:00:00 2001 From: chenhao Date: Tue, 27 Jan 2026 18:04:44 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(quotation):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=8A=A5=E4=BB=B7=E5=8D=95=E5=8A=9F=E8=83=BD=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增报价单管理界面,支持报价单的增删改查操作 - 实现报价单基本信息设置和产品配置功能 - 添加报价单号自动生成机制,支持按日期和序列号生成 - 集成代表处选择和客户信息管理功能 - 实现多步骤表单流程,支持基本信息和配置信息分步填写 - 添加产品列表管理,支持软件、硬件和服务产品的分类配置 - 实现报价金额和折后金额的自动计算功能 - 优化发货记录中的服务年限计算逻辑 - 修复代码生成器中编号从0开始的问题,调整为从1开始 --- oms_web/oms_vue/src/api/base/quotation.js | 44 ++ .../src/views/base/quotation/index.vue | 497 ++++++++++++++++++ .../sip/controller/QuotationController.java | 72 +++ .../com/ruoyi/sip/domain/CodeGenTable.java | 1 + .../java/com/ruoyi/sip/domain/Quotation.java | 83 +++ .../sip/domain/QuotationProductInfo.java | 79 +++ .../com/ruoyi/sip/mapper/QuotationMapper.java | 50 ++ .../mapper/QuotationProductInfoMapper.java | 54 ++ .../service/IQuotationProductInfoService.java | 51 ++ .../ruoyi/sip/service/IQuotationService.java | 49 ++ .../service/impl/CodeGenTableServiceImpl.java | 9 +- .../impl/InventoryDeliveryServiceImpl.java | 1 + .../impl/QuotationProductInfoServiceImpl.java | 100 ++++ .../service/impl/QuotationServiceImpl.java | 127 +++++ .../mapper/Quotation/QuotationMapper.xml | 298 +++++++++++ .../QuotationProductInfoMapper.xml | 339 ++++++++++++ 16 files changed, 1852 insertions(+), 2 deletions(-) create mode 100644 oms_web/oms_vue/src/api/base/quotation.js create mode 100644 oms_web/oms_vue/src/views/base/quotation/index.vue create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/domain/QuotationProductInfo.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationMapper.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationProductInfoMapper.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationProductInfoService.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationService.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationProductInfoServiceImpl.java create mode 100644 ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java create mode 100644 ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml create mode 100644 ruoyi-sip/src/main/resources/mapper/QuotationProductInfo/QuotationProductInfoMapper.xml diff --git a/oms_web/oms_vue/src/api/base/quotation.js b/oms_web/oms_vue/src/api/base/quotation.js new file mode 100644 index 00000000..24485517 --- /dev/null +++ b/oms_web/oms_vue/src/api/base/quotation.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询报价单列表 +export function listQuotation(query) { + return request({ + url: '/quotation/list', + method: 'get', + params: query + }) +} + +// 查询报价单详细 +export function getQuotation(id) { + return request({ + url: '/quotation/' + id, + method: 'get' + }) +} + +// 新增报价单 +export function addQuotation(data) { + return request({ + url: '/quotation/insert', + method: 'post', + data: data + }) +} + +// 修改报价单 +export function updateQuotation(data) { + return request({ + url: '/quotation/update', + method: 'put', + data: data + }) +} + +// 删除报价单 +export function delQuotation(id) { + return request({ + url: '/quotation/' + id, + method: 'delete' + }) +} diff --git a/oms_web/oms_vue/src/views/base/quotation/index.vue b/oms_web/oms_vue/src/views/base/quotation/index.vue new file mode 100644 index 00000000..83e6b25e --- /dev/null +++ b/oms_web/oms_vue/src/views/base/quotation/index.vue @@ -0,0 +1,497 @@ + + + diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java new file mode 100644 index 00000000..8a88a6d8 --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java @@ -0,0 +1,72 @@ +package com.ruoyi.sip.controller; + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.sip.domain.Quotation; +import com.ruoyi.sip.service.IQuotationService; +import org.springframework.web.bind.annotation.*; + +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +/** + * @Author makejava + * @Desc 报价单 + * (Quotation)表控制层 + * @Date 2026-01-26 14:58:02 + */ + + +@RestController +@RequestMapping("/quotation") + +public class QuotationController extends BaseController { + + @Autowired + private IQuotationService quotationService; + + + @GetMapping("/list") + public TableDataInfo list(Quotation quotation) { + startPage(); + List list = quotationService.queryAll(quotation); + return getDataTable(list); + } + + + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Integer id) { + return AjaxResult.success(quotationService.queryById(id)); + } + + + + @PostMapping("/insert") + public AjaxResult add(@RequestBody Quotation quotation) { + return toAjax(quotationService.insert(quotation)); + } + + + + @PutMapping("/update") + public AjaxResult edit(@RequestBody Quotation quotation) { + return toAjax(quotationService.update(quotation)); + } + + + + @DeleteMapping("/{id}") + public AjaxResult remove(@PathVariable("id") Integer id) { + return toAjax(quotationService.deleteById(id)); + } + + /** + * 通过主键批量删除报价单 + + */ + @DeleteMapping("/remove/batch/{ids}") + public AjaxResult batchRemove(@PathVariable("ids") Integer[] ids) { + return AjaxResult.success(quotationService.batchRemove(ids)); + } +} diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/CodeGenTable.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/CodeGenTable.java index 0636fb62..340e464d 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/CodeGenTable.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/CodeGenTable.java @@ -81,6 +81,7 @@ public class CodeGenTable { @Getter public enum TableNameEnum{ ORDER_DELIVERY("order_delivery", "发货记录"), + OMS_QUOTATION("oms_quotation", "报价单"), ; private final String name; private final String desc; diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java new file mode 100644 index 00000000..4402711d --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java @@ -0,0 +1,83 @@ +package com.ruoyi.sip.domain; + +import java.util.Date; +import java.util.List; + + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + +/** + * 报价单 + * (Quotation)实体类 + * + * @author makejava + * @since 2026-01-27 15:11:57 + */ +@Data +public class Quotation extends BaseEntity { + + private Integer id; + /** + * 报价单号 + */ + private String quotationCode; + /** + * 名称 + */ + private String quotationName; + /** + * 项目编号 + */ + private String projectCode; + /** + * 报价金额 + */ + private Double quotationAmount; + /** + * 折后金额 + */ + private Double discountAmount; + /** + * 状态 + */ + private String quotationStatus; + + private Date createTime; + + private String createBy; + + private String updateBy; + + private Date updateTime; + + private String remark; + /** + * 项目id 多个 + */ + private String projectId; + /** + * 代表处 + */ + private String agentCode; + /** + * 币种 + */ + private String amountType; + private Date createTimeStart; + private Date createTimeEnd; + /** + * 客户名称 + */ + private String customerName; +// @Excel(name = "软件") + private List softwareProjectProductInfoList; +// @Excel(name = "终端") + private List hardwareProjectProductInfoList; +// @Excel(name = "服务") + private List maintenanceProjectProductInfoList; +} + + + diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/QuotationProductInfo.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/QuotationProductInfo.java new file mode 100644 index 00000000..e5c43937 --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/QuotationProductInfo.java @@ -0,0 +1,79 @@ +package com.ruoyi.sip.domain; + + +import com.ruoyi.common.core.domain.BaseEntity; +import lombok.Data; + + +/** + * 项目产品信息(QuotationProductInfo)实体类 + * + * @author makejava + * @since 2026-01-27 14:45:17 + */ +@Data +public class QuotationProductInfo extends BaseEntity { + + private Integer id; + /** + * 产品编码 + */ + private String productBomCode; + /** + * 产品型号 + */ + private String model; + /** + * 产品代码 + */ + private String productCode; + /** + * 产品描述 + */ + private String productDesc; + /** + * 产品数量 + */ + private Double quantity; + /** + * 目录单价 + */ + private Double cataloguePrice; + /** + * 目录总价 + */ + private Double catalogueAllPrice; + /** + * 单价 + */ + private Double price; + /** + * 总价 + */ + private Double allPrice; + /** + * 指导折扣 + */ + private Double guidanceDiscount; + /** + * 折扣 + */ + private Double discount; + /** + * 备注 + */ + private String remark; + /** + * 税率 + */ + private Double taxRate; + /** + * 配置器id + */ + private Integer quotationId; + private String type; + +} + + + diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationMapper.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationMapper.java new file mode 100644 index 00000000..e8494a90 --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationMapper.java @@ -0,0 +1,50 @@ +package com.ruoyi.sip.mapper; + +import com.ruoyi.sip.domain.Quotation; + +import java.util.List; + +/** + * @Author makejava + * @Desc 报价单 + * (Quotation)表数据库访问层 + * @Date 2026-01-26 14:58:02 + */ +public interface QuotationMapper { + + /** + * 通过实体作为筛选条件查询 + * + * @param quotation 实例对象 + * @return 对象列表 + */ + List queryAll(Quotation quotation); + + /** + * 根据ID查详情 + */ + Quotation queryById(Integer id); + + + /** + * 新增数据 + */ + int insert(Quotation quotation); + + + /** + * 修改数据 + */ + int update(Quotation quotation); + + /** + * 通过主键删除数据 + */ + int deleteById(Integer id); + + /** + * 通过id批量删除报价单 + */ + int batchRemove(Integer[] ids); + +} diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationProductInfoMapper.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationProductInfoMapper.java new file mode 100644 index 00000000..a72058cc --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/mapper/QuotationProductInfoMapper.java @@ -0,0 +1,54 @@ +package com.ruoyi.sip.mapper; + +import com.ruoyi.sip.domain.QuotationProductInfo; + +import java.util.List; + +/** + * @Author makejava + * @Desc 项目产品信息(QuotationProductInfo)表数据库访问层 + * @Date 2026-01-27 14:45:17 + */ +public interface QuotationProductInfoMapper { + + /** + * 通过实体作为筛选条件查询 + * + * @param quotationProductInfo 实例对象 + * @return 对象列表 + */ + List queryAll(QuotationProductInfo quotationProductInfo); + + /** + * 根据ID查详情 + */ + QuotationProductInfo queryById(Integer id); + + + /** + * 新增数据 + */ + int insert(QuotationProductInfo quotationProductInfo); + + + /** + * 修改数据 + */ + int update(QuotationProductInfo quotationProductInfo); + + /** + * 通过主键删除数据 + */ + int deleteById(Integer id); + + /** + * 通过id批量删除项目产品信息 + */ + int batchRemove(Integer[] ids); + + void insertBatch(List addList); + + void updateBatch(List addList); + + List listByQuotationId(List integers); +} diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationProductInfoService.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationProductInfoService.java new file mode 100644 index 00000000..5a5ec7da --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationProductInfoService.java @@ -0,0 +1,51 @@ +package com.ruoyi.sip.service; + +import com.ruoyi.sip.domain.QuotationProductInfo; + +import java.util.List; + +/** + * @Author makejava + * @Desc 项目产品信息(QuotationProductInfo)表服务接口 + * @Date 2026-01-27 14:45:18 + */ +public interface IQuotationProductInfoService { + + /** + * 通过实体作为筛选条件查询 + */ + List queryAll(QuotationProductInfo quotationProductInfo); + + + /** + * 根据ID查详情 + */ + QuotationProductInfo queryById(Integer id); + + /** + * 新增数据 + */ + int insert(QuotationProductInfo quotationProductInfo); + + /** + * 修改数据 + */ + int update(QuotationProductInfo quotationProductInfo); + + /** + * 通过主键删除数据 + */ + int deleteById(Integer id); + + /** + * 通过id批量删除项目产品信息 + */ + int batchRemove(Integer[] ids); + + void saveBatch(List productList); + + List listByQuotationId(List integers); +} + + + diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationService.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationService.java new file mode 100644 index 00000000..b2e91a19 --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/IQuotationService.java @@ -0,0 +1,49 @@ +package com.ruoyi.sip.service; + +import com.ruoyi.sip.domain.Quotation; + +import java.util.List; + +/** + * @Author makejava + * @Desc 报价单 + * (Quotation)表服务接口 + * @Date 2026-01-26 14:58:02 + */ +public interface IQuotationService { + + /** + * 通过实体作为筛选条件查询 + */ + List queryAll(Quotation quotation); + + + /** + * 根据ID查详情 + */ + Quotation queryById(Integer id); + + /** + * 新增数据 + */ + int insert(Quotation quotation); + + /** + * 修改数据 + */ + int update(Quotation quotation); + + /** + * 通过主键删除数据 + */ + int deleteById(Integer id); + + /** + * 通过id批量删除报价单 + */ + int batchRemove(Integer[] ids); + +} + + + diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/CodeGenTableServiceImpl.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/CodeGenTableServiceImpl.java index 824d3fea..3ab65a1f 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/CodeGenTableServiceImpl.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/CodeGenTableServiceImpl.java @@ -86,7 +86,7 @@ public class CodeGenTableServiceImpl implements ICodeGenTableService { updateDto.setVersion(codeGeneratorConfig.getVersion()); if (!DateUtils.isSameDay(codeGeneratorConfig.getGenDate(), DateUtils.getNowDate())) { - updateDto.setNumber(0); + updateDto.setNumber(1); updateDto.setGenDate(DateUtils.getNowDate()); } else { updateDto.setNumber(codeGeneratorConfig.getNumber()); @@ -96,8 +96,13 @@ public class CodeGenTableServiceImpl implements ICodeGenTableService { int currentSequence = updateDto.getNumber(); String sequenceStr = String.format("%0" + numberLength + "d", currentSequence); + String generatedCode; // 构造唯一 code - String generatedCode = prefix + DELIMIT + datePart + DELIMIT + sequenceStr; + if (StringUtils.isEmpty( prefix)){ + generatedCode = datePart + DELIMIT + sequenceStr; + }else { + generatedCode = prefix + DELIMIT + datePart + DELIMIT + sequenceStr; + } // 更新数据库中的 number 字段,确保并发安全性 int updatedRows = codeGenTableMapper.updateNumber(updateDto); diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/InventoryDeliveryServiceImpl.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/InventoryDeliveryServiceImpl.java index c1f7aa2f..e7935a8b 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/InventoryDeliveryServiceImpl.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/InventoryDeliveryServiceImpl.java @@ -468,6 +468,7 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService { private Date updateStartTimeAndAddList(DeliveryInfoVo deliveryInfoVo, ProjectProductInfo productInfo, Date startTime, List serviceInfoList) { int year = productInfo.getValue() == null ? 0 : Integer.parseInt(productInfo.getValue()); + year= Math.toIntExact(year * (productInfo.getQuantity()==null? 1:productInfo.getQuantity())); DeliveryInfoVo.ServiceInfo serviceInfo = deliveryInfoVo.new ServiceInfo(); serviceInfo.setServiceLevel(productInfo.getProductBomCode()); serviceInfo.setServiceDescribe(productInfo.getProductDesc()); diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationProductInfoServiceImpl.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationProductInfoServiceImpl.java new file mode 100644 index 00000000..285af7ef --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationProductInfoServiceImpl.java @@ -0,0 +1,100 @@ +package com.ruoyi.sip.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.sip.domain.QuotationProductInfo; +import com.ruoyi.sip.mapper.QuotationProductInfoMapper; +import com.ruoyi.sip.service.IQuotationProductInfoService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @Author makejava + * @Desc 项目产品信息(QuotationProductInfo)表服务实现类 + * @Date 2026-01-27 14:45:18 + */ + +@Service +public class QuotationProductInfoServiceImpl implements IQuotationProductInfoService { + + @Resource + private QuotationProductInfoMapper quotationProductInfoMapper; + + + /** + * 查询列表数据 + * + * @param quotationProductInfo 实例对象 + * @return 对象列表 + */ + @Override + public List queryAll(QuotationProductInfo quotationProductInfo) { + List dataList = quotationProductInfoMapper.queryAll(quotationProductInfo); + return dataList; + } + + @Override + public QuotationProductInfo queryById(Integer id) { + return quotationProductInfoMapper.queryById(id); + } + + + @Override + public int insert(QuotationProductInfo quotationProductInfo) { + return quotationProductInfoMapper.insert(quotationProductInfo); + } + + + @Override + public int update(QuotationProductInfo quotationProductInfo) { + return quotationProductInfoMapper.update(quotationProductInfo); + } + + + @Override + public int deleteById(Integer id) { + return quotationProductInfoMapper.deleteById(id); + } + + /** + * 通过id批量删除项目产品信息 + */ + @Override + public int batchRemove(Integer[] ids) { + return quotationProductInfoMapper.batchRemove(ids); + } + + @Override + public void saveBatch(List productList) { + if (CollUtil.isEmpty(productList)) { + //保存详情 + return; + } + List addList = productList.stream().filter(quotationProductInfo -> quotationProductInfo.getId() == null).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(addList)){ + //保存详情 + quotationProductInfoMapper.insertBatch(addList); + + + } + List updateList = productList.stream().filter(quotationProductInfo -> quotationProductInfo.getId() != null).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(updateList)){ + //保存详情 + quotationProductInfoMapper.updateBatch(updateList); + } + + + } + + @Override + public List listByQuotationId(List integers) { + return quotationProductInfoMapper.listByQuotationId(integers); + } + +} + + + diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java new file mode 100644 index 00000000..a19c4e9a --- /dev/null +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java @@ -0,0 +1,127 @@ +package com.ruoyi.sip.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.sip.domain.*; +import com.ruoyi.sip.mapper.QuotationMapper; +import com.ruoyi.sip.service.ICodeGenTableService; +import com.ruoyi.sip.service.IQuotationProductInfoService; +import com.ruoyi.sip.service.IQuotationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @Author makejava + * @Desc 报价单 + * (Quotation)表服务实现类 + * @Date 2026-01-26 14:58:02 + */ + +@Service +@Transactional(rollbackFor = Exception.class) +public class QuotationServiceImpl implements IQuotationService { + + @Resource + private QuotationMapper quotationMapper; + @Autowired + private ICodeGenTableService codeGenTableService; + + @Autowired + private IQuotationProductInfoService quotationProductInfoService; + + + /** + * 查询列表数据 + * + * @param quotation 实例对象 + * @return 对象列表 + */ + @Override + public List queryAll(Quotation quotation) { + List dataList = quotationMapper.queryAll(quotation); + return dataList; + } + + @Override + public Quotation queryById(Integer id) { + Quotation quotation = quotationMapper.queryById(id); + if (quotation != null) { + List productInfoList = quotationProductInfoService.listByQuotationId(Collections.singletonList(quotation.getId())); + Map> productListMap = productInfoList.stream().collect(Collectors.groupingBy(QuotationProductInfo::getType)); + quotation.setSoftwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.SOFTWARE.getType())); + quotation.setHardwareProjectProductInfoList(productListMap.get(ProductInfo.ProductTypeEnum.HARDWARE.getType())); + List maintenanceProjectProductInfoList = productListMap.getOrDefault(ProductInfo.ProductTypeEnum.HARDWARE_MAINTENANCE.getType(), new ArrayList<>()); + maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE_MAINTENANCE.getType(), new ArrayList<>())); + maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.OTHER.getType(), new ArrayList<>())); + quotation.setMaintenanceProjectProductInfoList(maintenanceProjectProductInfoList); + } + return quotation; + } + + + @Override + public int insert(Quotation quotation) { + String s = codeGenTableService.generateCode(CodeGenTable.TableNameEnum.OMS_QUOTATION.getName()); + quotation.setQuotationCode(s); + int insert = quotationMapper.insert(quotation); + List productList = new ArrayList<>(); + if (CollUtil.isNotEmpty(quotation.getSoftwareProjectProductInfoList())) { + productList.addAll(quotation.getSoftwareProjectProductInfoList()); + } + if (CollUtil.isNotEmpty(quotation.getHardwareProjectProductInfoList())) { + productList.addAll(quotation.getHardwareProjectProductInfoList()); + } + if (CollUtil.isNotEmpty(quotation.getMaintenanceProjectProductInfoList())) { + productList.addAll(quotation.getMaintenanceProjectProductInfoList()); + } + for (QuotationProductInfo quotationProductInfo : productList) { + quotationProductInfo.setQuotationId(quotation.getId()); + } + quotationProductInfoService.saveBatch(productList); + return insert; + } + + + @Override + public int update(Quotation quotation) { + List productList = new ArrayList<>(); + if (CollUtil.isNotEmpty(quotation.getSoftwareProjectProductInfoList())) { + productList.addAll(quotation.getSoftwareProjectProductInfoList()); + } + if (CollUtil.isNotEmpty(quotation.getHardwareProjectProductInfoList())) { + productList.addAll(quotation.getHardwareProjectProductInfoList()); + } + if (CollUtil.isNotEmpty(quotation.getMaintenanceProjectProductInfoList())) { + productList.addAll(quotation.getMaintenanceProjectProductInfoList()); + } + for (QuotationProductInfo quotationProductInfo : productList) { + quotationProductInfo.setQuotationId(quotation.getId()); + } + + + quotationProductInfoService.saveBatch(productList); + return quotationMapper.update(quotation); + } + + + @Override + public int deleteById(Integer id) { + return quotationMapper.deleteById(id); + } + + /** + * 通过id批量删除报价单 + */ + @Override + public int batchRemove(Integer[] ids) { + return quotationMapper.batchRemove(ids); + } + +} + + + diff --git a/ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml b/ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml new file mode 100644 index 00000000..b3d4bd61 --- /dev/null +++ b/ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml @@ -0,0 +1,298 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + id + , quotation_code, quotation_name, project_code, quotation_amount, discount_amount, quotation_status, create_time, create_by, update_by, update_time, remark, project_id, agent_code, amount_type, customer_name + + + + + + + + + + + + + INSERT INTO oms_quotation + + + quotation_code, + + + quotation_name, + + + project_code, + + + quotation_amount, + + + discount_amount, + + + quotation_status, + + + create_time, + + + create_by, + + + update_by, + + + update_time, + + + remark, + + + project_id, + + + agent_code, + + + amount_type, + + + customer_name, + + + + + #{quotationCode}, + + + #{quotationName}, + + + #{projectCode}, + + + #{quotationAmount}, + + + #{discountAmount}, + + + #{quotationStatus}, + + + #{createTime}, + + + #{createBy}, + + + #{updateBy}, + + + #{updateTime}, + + + #{remark}, + + + #{projectId}, + + + #{agentCode}, + + + #{amountType}, + + + #{customerName}, + + + + + + + UPDATE oms_quotation + + + quotation_code = #{quotationCode}, + + + quotation_name = #{quotationName}, + + + project_code = #{projectCode}, + + + quotation_amount = #{quotationAmount}, + + + discount_amount = #{discountAmount}, + + + quotation_status = #{quotationStatus}, + + + create_time = #{createTime}, + + + create_by = #{createBy}, + + + update_by = #{updateBy}, + + + update_time = #{updateTime}, + + + remark = #{remark}, + + + project_id = #{projectId}, + + + agent_code = #{agentCode}, + + + amount_type = #{amountType}, + + + customer_name = #{customerName}, + + + WHERE id = #{id} + + + + + DELETE + FROM oms_quotation + WHERE id = #{id} + + + + + delete from oms_quotation where id in + + #{id} + + + + + + + diff --git a/ruoyi-sip/src/main/resources/mapper/QuotationProductInfo/QuotationProductInfoMapper.xml b/ruoyi-sip/src/main/resources/mapper/QuotationProductInfo/QuotationProductInfoMapper.xml new file mode 100644 index 00000000..23267c90 --- /dev/null +++ b/ruoyi-sip/src/main/resources/mapper/QuotationProductInfo/QuotationProductInfoMapper.xml @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + SELECT t1.id, + t1.product_bom_code, + t1.model, + t1.product_code, + t1.product_desc, + t1.quantity, + t1.catalogue_price, + t1.catalogue_all_price, + t1.price, + t1.all_price, + t1.guidance_discount, + t1.discount, + t1.remark, + t1.tax_rate, + t1.quotation_id, + t2.type + FROM oms_quotation_product_info t1 + left join product_info t2 on t1.product_bom_code=t2.product_code + + + + + + + + + + + + + + INSERT INTO oms_quotation_product_info + + + product_bom_code, + + + model, + + + product_code, + + + product_desc, + + + quantity, + + + catalogue_price, + + + catalogue_all_price, + + + price, + + + all_price, + + + guidance_discount, + + + discount, + + + remark, + + + tax_rate, + + + quotation_id, + + + + + #{productBomCode}, + + + #{model}, + + + #{productCode}, + + + #{productDesc}, + + + #{quantity}, + + + #{cataloguePrice}, + + + #{catalogueAllPrice}, + + + #{price}, + + + #{allPrice}, + + + #{guidanceDiscount}, + + + #{discount}, + + + #{remark}, + + + #{taxRate}, + + + #{quotationId}, + + + + + INSERT INTO oms_quotation_product_info(product_bom_code, model, product_code, product_desc, + quantity, catalogue_price, catalogue_all_price, price, + all_price, guidance_discount, discount, remark, tax_rate, quotation_id) + + VALUES + + ( + #{item.productBomCode}, #{item.model}, #{item.productCode},#{item.productDesc}, + #{item.quantity},#{item.cataloguePrice},#{item.catalogueAllPrice},#{item.price}, + #{item.allPrice},#{item.guidanceDiscount},#{item.discount},#{item.remark},#{item.taxRate},#{item.quotationId} + ) + + + + + + UPDATE oms_quotation_product_info + + + product_bom_code = #{productBomCode}, + + + model = #{model}, + + + product_code = #{productCode}, + + + product_desc = #{productDesc}, + + + quantity = #{quantity}, + + + catalogue_price = #{cataloguePrice}, + + + catalogue_all_price = #{catalogueAllPrice}, + + + price = #{price}, + + + all_price = #{allPrice}, + + + guidance_discount = #{guidanceDiscount}, + + + discount = #{discount}, + + + remark = #{remark}, + + + tax_rate = #{taxRate}, + + + quotation_id = #{quotationId}, + + + WHERE id = #{id} + + + + UPDATE oms_quotation_product_info + + + product_bom_code = #{item.productBomCode}, + + + model = #{item.model}, + + + product_code = #{item.productCode}, + + + product_desc = #{item.productDesc}, + + + quantity = #{item.quantity}, + + + catalogue_price = #{item.cataloguePrice}, + + + catalogue_all_price = #{item.catalogueAllPrice}, + + + price = #{item.price}, + + + all_price = #{item.allPrice}, + + + guidance_discount = #{item.guidanceDiscount}, + + + discount = #{item.discount}, + + + remark = #{item.remark}, + + + tax_rate = #{item.taxRate}, + + + quotation_id = #{item.quotationId}, + + + WHERE id = #{item.id} + + + + + + + DELETE + FROM oms_quotation_product_info + WHERE id = #{id} + + + + + delete from oms_quotation_product_info where id in + + #{id} + + + + + + + From 61b10eba26107393b1812c8b5d3af1f451d7726d Mon Sep 17 00:00:00 2001 From: chenhao Date: Thu, 29 Jan 2026 18:07:31 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat(quotation):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=8A=A5=E4=BB=B7=E5=8D=95=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在报价单列表页面添加导出按钮,支持单个报价单导出为Excel文件 - 实现exportSingle方法,支持按ID导出指定报价单数据 - 集成EasyExcel库,实现报价单数据的Excel格式化导出 - 添加报价单导出的API接口和权限控制 - 实现包含Logo、标题、产品明细等完整报价单格式的Excel文件生成 - 添加导出功能的前端调用和下载处理逻辑 --- oms_web/oms_vue/src/api/base/quotation.js | 6 + .../src/views/base/quotation/index.vue | 17 +- .../sip/controller/QuotationController.java | 4 + .../ruoyi/sip/service/IQuotationService.java | 1 + .../service/impl/QuotationServiceImpl.java | 293 +++++++++++++++++- 5 files changed, 318 insertions(+), 3 deletions(-) diff --git a/oms_web/oms_vue/src/api/base/quotation.js b/oms_web/oms_vue/src/api/base/quotation.js index 24485517..ddd8d6ad 100644 --- a/oms_web/oms_vue/src/api/base/quotation.js +++ b/oms_web/oms_vue/src/api/base/quotation.js @@ -42,3 +42,9 @@ export function delQuotation(id) { method: 'delete' }) } +export function exportSingleQuotation(id) { + return request({ + url: '/quotation/export/single/' + id, + method: 'get' + }) +} diff --git a/oms_web/oms_vue/src/views/base/quotation/index.vue b/oms_web/oms_vue/src/views/base/quotation/index.vue index 83e6b25e..10de1091 100644 --- a/oms_web/oms_vue/src/views/base/quotation/index.vue +++ b/oms_web/oms_vue/src/views/base/quotation/index.vue @@ -108,6 +108,13 @@ @click="handleDelete(scope.row)" v-hasPermi="['base:quotation:remove']" >删除 + 导出 @@ -215,10 +222,11 @@ + diff --git a/oms_web/oms_vue/src/views/base/quotation/index.vue b/oms_web/oms_vue/src/views/base/quotation/index.vue index 65e5baae..6b12fe85 100644 --- a/oms_web/oms_vue/src/views/base/quotation/index.vue +++ b/oms_web/oms_vue/src/views/base/quotation/index.vue @@ -50,7 +50,7 @@ icon="el-icon-plus" size="mini" @click="handleAdd" - v-hasPermi="['base:quotation:add']" + v-hasPermi="['sip:quotation:add']" >新增 @@ -61,7 +61,7 @@ size="mini" :disabled="single" @click="handleUpdate" - v-hasPermi="['base:quotation:edit']" + v-hasPermi="['sip:quotation:update']" >修改 @@ -72,7 +72,7 @@ size="mini" :disabled="multiple" @click="handleDelete" - v-hasPermi="['base:quotation:remove']" + v-hasPermi="['sip:quotation:delete']" >删除 @@ -103,35 +103,34 @@ type="text" icon="el-icon-view" @click="handleDetail(scope.row)" - v-hasPermi="['base:quotation:query']" >详情 修改 复制创建 删除 导出 diff --git a/oms_web/oms_vue/src/views/base/quotation/selectQuotation.vue b/oms_web/oms_vue/src/views/base/quotation/selectQuotation.vue new file mode 100644 index 00000000..68449e29 --- /dev/null +++ b/oms_web/oms_vue/src/views/base/quotation/selectQuotation.vue @@ -0,0 +1,126 @@ + + + diff --git a/oms_web/oms_vue/src/views/project/info/ProjectForm.vue b/oms_web/oms_vue/src/views/project/info/ProjectForm.vue index e1171e82..06e2e0d7 100644 --- a/oms_web/oms_vue/src/views/project/info/ProjectForm.vue +++ b/oms_web/oms_vue/src/views/project/info/ProjectForm.vue @@ -433,7 +433,7 @@ - + @@ -683,6 +683,7 @@ export default { partnerName: null, partnerCode: null, partnerUserName: null, + partnerSystemUserId: null, contactWay: null, estimatedAmount: null, estimatedOrderTime: null, @@ -834,6 +835,7 @@ export default { if (value === 'h3c') { this.form.partnerName = '新华三'; this.form.partnerCode = null; + this.form.partnerSystemUserId = null; } if (this.$refs.form) { this.$nextTick(() => { @@ -923,6 +925,8 @@ export default { this.form.partnerCode = partner.partnerCode; this.form.partnerUserName = partner.contactPerson; this.form.contactWay = partner.contactPhone; + this.$set(this.form,'partnerSystemUserId', partner.systemUserId); + this.selectPartnerVisible = false; }, openSelectPeople() { diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java index 936d4ac3..346dd833 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/controller/QuotationController.java @@ -5,6 +5,7 @@ import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.sip.domain.Quotation; import com.ruoyi.sip.service.IQuotationService; +import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.web.bind.annotation.*; import org.springframework.beans.factory.annotation.Autowired; @@ -28,6 +29,7 @@ public class QuotationController extends BaseController { @GetMapping("/list") + @RequiresPermissions("sip:quotation:list") public TableDataInfo list(Quotation quotation) { startPage(); List list = quotationService.queryAll(quotation); @@ -43,6 +45,7 @@ public class QuotationController extends BaseController { @PostMapping("/insert") + @RequiresPermissions("sip:quotation:add") public AjaxResult add(@RequestBody Quotation quotation) { return toAjax(quotationService.insert(quotation)); } @@ -50,6 +53,7 @@ public class QuotationController extends BaseController { @PutMapping("/update") + @RequiresPermissions("sip:quotation:update") public AjaxResult edit(@RequestBody Quotation quotation) { return toAjax(quotationService.update(quotation)); } @@ -57,6 +61,7 @@ public class QuotationController extends BaseController { @DeleteMapping("/{id}") + @RequiresPermissions("sip:quotation:delete") public AjaxResult remove(@PathVariable("id") Integer id) { return toAjax(quotationService.deleteById(id)); } @@ -66,10 +71,12 @@ public class QuotationController extends BaseController { */ @DeleteMapping("/remove/batch/{ids}") + @RequiresPermissions("sip:quotation:delete") public AjaxResult batchRemove(@PathVariable("ids") Integer[] ids) { return AjaxResult.success(quotationService.batchRemove(ids)); } @GetMapping("/export/single/{id}") + @RequiresPermissions("sip:quotation:export") public AjaxResult exportSingle(@PathVariable("id") Integer id) { return AjaxResult.success(quotationService.exportSingle(id)); } diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/ProjectInfo.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/ProjectInfo.java index 56d1555c..4010473b 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/ProjectInfo.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/ProjectInfo.java @@ -230,6 +230,7 @@ public class ProjectInfo extends BaseEntity private List projectFileList; private String fileId; + private String partnerSystemUserId; private Integer quotationId; private List quotationIdList; diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java index 440a7f31..9ab86553 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/domain/Quotation.java @@ -32,6 +32,7 @@ public class Quotation extends BaseEntity { * 项目编号 */ private String projectCode; + private String projectName; /** * 报价金额 */ @@ -78,6 +79,8 @@ public class Quotation extends BaseEntity { private List hardwareProjectProductInfoList; // @Excel(name = "服务") private List maintenanceProjectProductInfoList; + //省代 + private List provinceProductInfoList; @Getter public enum QuotationStatusEnum { diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java index 13a9dc81..790fcf5a 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/QuotationServiceImpl.java @@ -11,6 +11,7 @@ import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy; +import com.ruoyi.common.annotation.DataScope; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.ShiroUtils; @@ -69,6 +70,7 @@ public class QuotationServiceImpl implements IQuotationService { * @return 对象列表 */ @Override + @DataScope(deptAlias = "u", userAlias = "u") public List queryAll(Quotation quotation) { List dataList = quotationMapper.queryAll(quotation); PageUtils.clearPage(); @@ -95,7 +97,14 @@ public class QuotationServiceImpl implements IQuotationService { maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.SOFTWARE_MAINTENANCE.getType(), new ArrayList<>())); maintenanceProjectProductInfoList.addAll(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.OTHER.getType(), new ArrayList<>())); quotation.setMaintenanceProjectProductInfoList(maintenanceProjectProductInfoList); + quotation.setProvinceProductInfoList(productListMap.getOrDefault(ProductInfo.ProductTypeEnum.PROVINCE_SERVICE.getType(),new ArrayList<>())); + ProjectInfo projectInfo = new ProjectInfo(); + projectInfo.setQuotationId(quotation.getId()); + List projectInfos = projectInfoService.selectProjectInfoList(projectInfo); + quotation.setProjectCode(projectInfos.stream().map(ProjectInfo::getProjectCode).collect(Collectors.joining(","))); + quotation.setProjectName(projectInfos.stream().map(ProjectInfo::getProjectName).collect(Collectors.joining(","))); } + return quotation; } @@ -187,46 +196,42 @@ public class QuotationServiceImpl implements IQuotationService { row1.add(""); } rows.add(row1); + rows.add(Collections.emptyList()); // Calculate Date String validDate = LocalDate.now().plusDays(7).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // Helper to create padded row List row2 = new ArrayList<>(); - row2.add("项目名称:"); - row2.add(quotation.getQuotationName()); + row2.add(""); + row2.add("*项目名称:"); + row2.add(quotation.getProjectName()); row2.add(""); row2.add(""); row2.add(""); - row2.add(""); // Padding - row2.add("*本报价单有效期至:"); - row2.add(validDate); + row2.add("*本报价单有效期至:"+validDate); rows.add(row2); List row3 = new ArrayList<>(); + row3.add(""); row3.add("*项目ID:"); row3.add(quotation.getProjectCode()); - row3.add(""); - row3.add(""); + row3.add("(项目名称和ID在商务申请时必填)"); row3.add(""); row3.add(""); row3.add("*云桌面完整报价单必须包含部署服务、现场维保、省代集成服务,此三项由省代进行补充报价,不能缺项"); rows.add(row3); List row4 = new ArrayList<>(); + row4.add(""); row4.add("国家/地区 :"); row4.add("中国大陆"); row4.add(""); row4.add(""); row4.add(""); - row4.add(""); row4.add("*因上游CPU、内存、存储波动较大,封标前3天与汇智区域接口人邮件确定商务折扣和供货周期,否则报价单无效"); rows.add(row4); - List row5 = new ArrayList<>(); - row5.add("备注:"); - row5.add(quotation.getRemark()); - rows.add(row5); rows.add(Collections.emptyList()); @@ -236,18 +241,25 @@ public class QuotationServiceImpl implements IQuotationService { "目录单价(RMB)", "推荐折扣", "折扣单价(RMB)", "总价(RMB)", "目录总价(RMB)", "CID信息", "备注" ); - // 记录列标题行索引,使其变灰 + // 记录列标题行索引 + int headerRowIndex = rows.size(); Set coloredRowIndices = new HashSet<>(); - coloredRowIndices.add(rows.size()); + // 标题列填充灰色 + coloredRowIndices.add(headerRowIndex); rows.add(new ArrayList<>(headers)); // 3. 第三部分:数据分组 Set aquaRowIndices = new HashSet<>(); AtomicInteger sectionCounter = new AtomicInteger(1); - addSection(rows, "软件", quotation.getSoftwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter); - addSection(rows, "硬件", quotation.getHardwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter); - addSection(rows, "服务", quotation.getMaintenanceProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter); + // 添加默认分组:云桌面服务器 + QuotationProductInfo defaultItem = new QuotationProductInfo(); + + addSection(rows, "云桌面服务器","VOI/VDI服务器", Collections.singletonList(defaultItem), coloredRowIndices, aquaRowIndices, sectionCounter); + + addSection(rows, "云桌面软件", "Workspace/learningspace/NEX",quotation.getSoftwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter); + addSection(rows, "云终端", "VOI/VDI终端",quotation.getHardwareProjectProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter); + addSection(rows, "省代服务(若从省代出货需计算)","省代服务", quotation.getProvinceProductInfoList(), coloredRowIndices, aquaRowIndices, sectionCounter); ExcelUtil util = new ExcelUtil<>(Object.class); String fileName = util.encodingFilename("报价单_" + quotation.getQuotationCode()); @@ -257,15 +269,19 @@ public class QuotationServiceImpl implements IQuotationService { WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); contentWriteCellStyle.setWrapped(true); contentWriteCellStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); + contentWriteCellStyle.setHorizontalAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.LEFT); HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle); EasyExcel.write(filePath) .registerWriteHandler(horizontalCellStyleStrategy) - .registerWriteHandler(new CustomColumnWidthStrategy()) + .registerWriteHandler(new CustomColumnWidthStrategy(headerRowIndex)) .registerWriteHandler(new com.alibaba.excel.write.handler.CellWriteHandler() { private org.apache.poi.ss.usermodel.CellStyle coloredStyle; + private org.apache.poi.ss.usermodel.CellStyle coloredLeftStyle; private org.apache.poi.ss.usermodel.CellStyle aquaStyle; private org.apache.poi.ss.usermodel.CellStyle centerStyle; + private org.apache.poi.ss.usermodel.CellStyle infoStyle; + private org.apache.poi.ss.usermodel.CellStyle redInfoStyle; @Override public void beforeCellCreate(com.alibaba.excel.write.metadata.holder.WriteSheetHolder writeSheetHolder, com.alibaba.excel.write.metadata.holder.WriteTableHolder writeTableHolder, org.apache.poi.ss.usermodel.Row row, com.alibaba.excel.metadata.Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { @@ -289,6 +305,8 @@ public class QuotationServiceImpl implements IQuotationService { centerStyle = workbook.createCellStyle(); centerStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER); centerStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); + centerStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE.getIndex()); + centerStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); org.apache.poi.ss.usermodel.Font font = workbook.createFont(); font.setBold(true); font.setFontHeightInPoints((short) 16); @@ -297,21 +315,75 @@ public class QuotationServiceImpl implements IQuotationService { cell.setCellStyle(centerStyle); // Merge cells for title (0-10, Logo is at 11 usually, but let's merge fewer if needed, or 0-10) writeSheetHolder.getSheet().addMergedRegionUnsafe(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 10)); - } + } else if (cell.getRowIndex() < headerRowIndex) { + // Info rows (0 to headerRowIndex-1) - Excluding 0 handled above? + // Actually handle 0 separately or here. 0 is handled above. + if (cell.getRowIndex() == 0) return; - if (coloredRowIndices.contains(cell.getRowIndex())) { - if (coloredStyle == null) { - org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); - coloredStyle = workbook.createCellStyle(); - coloredStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex()); - coloredStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); - coloredStyle.setWrapText(true); - coloredStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); - org.apache.poi.ss.usermodel.Font font = workbook.createFont(); - font.setBold(true); - coloredStyle.setFont(font); + // Check for Red Font Conditions + boolean isRed = false; + if (cell.getRowIndex() == 2 && (cell.getColumnIndex() == 3 || cell.getColumnIndex() == 6)) { + isRed = true; + } else if (cell.getRowIndex() == 3 && (cell.getColumnIndex() == 6 || cell.getColumnIndex() == 3)) { + isRed = true; + } else if (cell.getRowIndex() == 4 && cell.getColumnIndex() == 6) { + isRed = true; + } + + if (isRed) { + if (redInfoStyle == null) { + org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + redInfoStyle = workbook.createCellStyle(); + redInfoStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE.getIndex()); + redInfoStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); + redInfoStyle.setWrapText(false); + redInfoStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); + org.apache.poi.ss.usermodel.Font font = workbook.createFont(); + font.setColor(org.apache.poi.ss.usermodel.IndexedColors.RED.getIndex()); + redInfoStyle.setFont(font); + } + cell.setCellStyle(redInfoStyle); + } else { + if (infoStyle == null) { + org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + infoStyle = workbook.createCellStyle(); + infoStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.WHITE.getIndex()); + infoStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); + infoStyle.setWrapText(false); + infoStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); + } + cell.setCellStyle(infoStyle); + } + + } else if (coloredRowIndices.contains(cell.getRowIndex())) { + if (cell.getRowIndex() > headerRowIndex) { + if (coloredLeftStyle == null) { + org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + coloredLeftStyle = workbook.createCellStyle(); + coloredLeftStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex()); + coloredLeftStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); + coloredLeftStyle.setWrapText(true); + coloredLeftStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); + coloredLeftStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.LEFT); + org.apache.poi.ss.usermodel.Font font = workbook.createFont(); + font.setBold(true); + coloredLeftStyle.setFont(font); + } + cell.setCellStyle(coloredLeftStyle); + } else { + if (coloredStyle == null) { + org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + coloredStyle = workbook.createCellStyle(); + coloredStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex()); + coloredStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); + coloredStyle.setWrapText(true); + coloredStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); + org.apache.poi.ss.usermodel.Font font = workbook.createFont(); + font.setBold(true); + coloredStyle.setFont(font); + } + cell.setCellStyle(coloredStyle); } - cell.setCellStyle(coloredStyle); } else if (aquaRowIndices.contains(cell.getRowIndex())) { if (aquaStyle == null) { org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); @@ -319,6 +391,7 @@ public class QuotationServiceImpl implements IQuotationService { aquaStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.AQUA.getIndex()); aquaStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND); aquaStyle.setWrapText(true); + aquaStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.LEFT); } cell.setCellStyle(aquaStyle); } @@ -350,7 +423,7 @@ public class QuotationServiceImpl implements IQuotationService { quotationMapper.update(quotation); } - private void addSection(List> rows, String title, List list, Set coloredRowIndices, Set aquaRowIndices, AtomicInteger sectionCounter) { + private void addSection(List> rows, String title, String subTitle,List list, Set coloredRowIndices, Set aquaRowIndices, AtomicInteger sectionCounter) { if (CollUtil.isEmpty(list)) { return; } @@ -367,6 +440,18 @@ public class QuotationServiceImpl implements IQuotationService { } rows.add(titleRow); + // 添加标题行 (补齐12列) + List subTitleRow = new ArrayList<>(); + subTitleRow.add(currentSection+"_"+currentSection); + subTitleRow.add(subTitle); + for (int i = 0; i < 10; i++) { + subTitleRow.add(""); + } + rows.add(subTitleRow); + + + + double sumAllPrice = 0.0; double sumCatalogueAllPrice = 0.0; @@ -374,7 +459,7 @@ public class QuotationServiceImpl implements IQuotationService { int index = 1; for (QuotationProductInfo item : list) { List row = new ArrayList<>(); - row.add(currentSection + "_" + index++); + row.add(""); row.add(item.getProductBomCode()); row.add(item.getModel()); row.add(item.getProductDesc()); @@ -400,7 +485,7 @@ public class QuotationServiceImpl implements IQuotationService { coloredRowIndices.add(rows.size()); List subTotalRow1 = new ArrayList<>(); subTotalRow1.add(""); - subTotalRow1.add(title); + subTotalRow1.add(subTitle); subTotalRow1.add(""); subTotalRow1.add(""); subTotalRow1.add(""); @@ -434,9 +519,19 @@ public class QuotationServiceImpl implements IQuotationService { // 自定义列宽策略:自适应但有最大宽度 public static class CustomColumnWidthStrategy extends AbstractColumnWidthStyleStrategy { private static final int MAX_COLUMN_WIDTH = 50; + private final int headerRowIndex; + + public CustomColumnWidthStrategy(int headerRowIndex) { + this.headerRowIndex = headerRowIndex; + } @Override protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + // 1-6行不参与列宽计算 (小于标题行的行) + if (cell.getRowIndex() < headerRowIndex) { + return; + } + if (isHead != null && isHead) { // 如果是表头,不需特别处理,EasyExcel会自动处理,或者这里也可以计算 return; diff --git a/ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml b/ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml index 72606a4c..252e9e59 100644 --- a/ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml +++ b/ruoyi-sip/src/main/resources/mapper/Quotation/QuotationMapper.xml @@ -28,70 +28,74 @@ diff --git a/ruoyi-sip/src/main/resources/mapper/sip/ProjectInfoMapper.xml b/ruoyi-sip/src/main/resources/mapper/sip/ProjectInfoMapper.xml index 4ceffc1f..a84de71d 100644 --- a/ruoyi-sip/src/main/resources/mapper/sip/ProjectInfoMapper.xml +++ b/ruoyi-sip/src/main/resources/mapper/sip/ProjectInfoMapper.xml @@ -91,6 +91,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" t3.user_name as hz_support_user_name, t5.level, t5.contact_email as partner_email, + t6.system_user_id as partner_system_user_id, t1.update_time , ifnull(t4.work_time,t1.update_time) as last_work_update_time from project_info t1 @@ -98,6 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" left join sys_user t3 on t1.hz_support_user=t3.user_id left join partner_info t5 on t1.partner_code=t5.partner_code left join (select max(work_time) work_time,project_id from project_work_progress group by project_id) t4 ON t1.id=t4.project_id + left join partner_info t6 on t1.partner_code=t6.partner_code