|
|
|
|
@ -1,17 +1,36 @@
|
|
|
|
|
package com.ruoyi.sip.service.impl;
|
|
|
|
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
import cn.hutool.core.io.IoUtil;
|
|
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
|
import com.alibaba.excel.EasyExcel;
|
|
|
|
|
import com.alibaba.excel.metadata.CellData;
|
|
|
|
|
import com.alibaba.excel.metadata.Head;
|
|
|
|
|
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
|
|
|
|
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.exception.ServiceException;
|
|
|
|
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
|
|
|
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
|
|
|
|
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 lombok.extern.slf4j.Slf4j;
|
|
|
|
|
import org.apache.poi.ss.usermodel.Cell;
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
import java.time.LocalDate;
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -23,6 +42,7 @@ import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
@Service
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
@Slf4j
|
|
|
|
|
public class QuotationServiceImpl implements IQuotationService {
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
@ -121,7 +141,276 @@ public class QuotationServiceImpl implements IQuotationService {
|
|
|
|
|
return quotationMapper.batchRemove(ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
@Override
|
|
|
|
|
public String exportSingle(Integer id) {
|
|
|
|
|
try {
|
|
|
|
|
Quotation quotation = this.queryById(id);
|
|
|
|
|
|
|
|
|
|
List<List<Object>> rows = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 加载Logo
|
|
|
|
|
byte[] logoBytes = null;
|
|
|
|
|
try (InputStream is = SpringUtils.getResource("classpath:static/img/companyLogo.png").getInputStream()) {
|
|
|
|
|
logoBytes = IoUtil.readBytes(is);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.warn("读取公司Logo失败", e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1. 第一部分:标题信息
|
|
|
|
|
// Row 1: Title
|
|
|
|
|
List<Object> row1 = new ArrayList<>();
|
|
|
|
|
row1.add("云桌面产品价格明细清单");
|
|
|
|
|
for (int i = 1; i < 11; i++) row1.add("");
|
|
|
|
|
if (logoBytes != null) {
|
|
|
|
|
row1.add(logoBytes);
|
|
|
|
|
} else {
|
|
|
|
|
row1.add("");
|
|
|
|
|
}
|
|
|
|
|
rows.add(row1);
|
|
|
|
|
|
|
|
|
|
// Calculate Date
|
|
|
|
|
String validDate = LocalDate.now().plusDays(7).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
|
|
|
|
|
|
|
|
|
|
// Helper to create padded row
|
|
|
|
|
List<Object> row2 = new ArrayList<>();
|
|
|
|
|
row2.add("项目名称:");
|
|
|
|
|
row2.add(quotation.getQuotationName());
|
|
|
|
|
row2.add(""); row2.add(""); row2.add(""); row2.add(""); // Padding
|
|
|
|
|
row2.add("*本报价单有效期至:");
|
|
|
|
|
row2.add(validDate);
|
|
|
|
|
rows.add(row2);
|
|
|
|
|
|
|
|
|
|
List<Object> row3 = new ArrayList<>();
|
|
|
|
|
row3.add("*项目ID:");
|
|
|
|
|
row3.add(quotation.getProjectCode());
|
|
|
|
|
row3.add(""); row3.add(""); row3.add(""); row3.add("");
|
|
|
|
|
row3.add("*云桌面完整报价单必须包含部署服务、现场维保、省代集成服务,此三项由省代进行补充报价,不能缺项");
|
|
|
|
|
rows.add(row3);
|
|
|
|
|
|
|
|
|
|
List<Object> row4 = new ArrayList<>();
|
|
|
|
|
row4.add("国家/地区 :");
|
|
|
|
|
row4.add("中国大陆");
|
|
|
|
|
row4.add(""); row4.add(""); row4.add(""); row4.add("");
|
|
|
|
|
row4.add("*因上游CPU、内存、存储波动较大,封标前3天与汇智区域接口人邮件确定商务折扣和供货周期,否则报价单无效");
|
|
|
|
|
rows.add(row4);
|
|
|
|
|
|
|
|
|
|
List<Object> row5 = new ArrayList<>();
|
|
|
|
|
row5.add("备注:");
|
|
|
|
|
row5.add(quotation.getRemark());
|
|
|
|
|
rows.add(row5);
|
|
|
|
|
|
|
|
|
|
rows.add(Collections.emptyList());
|
|
|
|
|
|
|
|
|
|
// 2. 第二部分:列标题
|
|
|
|
|
List<String> headers = Arrays.asList(
|
|
|
|
|
"序号", "产品编码", "产品型号", "产品代码描述", "数量",
|
|
|
|
|
"目录单价(RMB)", "推荐折扣", "折扣单价(RMB)", "总价(RMB)",
|
|
|
|
|
"目录总价(RMB)", "CID信息", "备注"
|
|
|
|
|
);
|
|
|
|
|
// 记录列标题行索引,使其变灰
|
|
|
|
|
Set<Integer> coloredRowIndices = new HashSet<>();
|
|
|
|
|
coloredRowIndices.add(rows.size());
|
|
|
|
|
rows.add(new ArrayList<>(headers));
|
|
|
|
|
|
|
|
|
|
// 3. 第三部分:数据分组
|
|
|
|
|
Set<Integer> 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);
|
|
|
|
|
|
|
|
|
|
ExcelUtil<Object> util = new ExcelUtil<>(Object.class);
|
|
|
|
|
String fileName = util.encodingFilename("报价单_" + quotation.getQuotationCode());
|
|
|
|
|
String filePath = util.getAbsoluteFile(fileName);
|
|
|
|
|
|
|
|
|
|
// 自动换行样式
|
|
|
|
|
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
|
|
|
|
contentWriteCellStyle.setWrapped(true);
|
|
|
|
|
contentWriteCellStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
|
|
|
|
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle);
|
|
|
|
|
|
|
|
|
|
EasyExcel.write(filePath)
|
|
|
|
|
.registerWriteHandler(horizontalCellStyleStrategy)
|
|
|
|
|
.registerWriteHandler(new CustomColumnWidthStrategy())
|
|
|
|
|
.registerWriteHandler(new com.alibaba.excel.write.handler.CellWriteHandler() {
|
|
|
|
|
private org.apache.poi.ss.usermodel.CellStyle coloredStyle;
|
|
|
|
|
private org.apache.poi.ss.usermodel.CellStyle aquaStyle;
|
|
|
|
|
private org.apache.poi.ss.usermodel.CellStyle centerStyle;
|
|
|
|
|
|
|
|
|
|
@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) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void afterCellCreate(com.alibaba.excel.write.metadata.holder.WriteSheetHolder writeSheetHolder, com.alibaba.excel.write.metadata.holder.WriteTableHolder writeTableHolder, org.apache.poi.ss.usermodel.Cell cell, com.alibaba.excel.metadata.Head head, Integer relativeRowIndex, Boolean isHead) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void afterCellDispose(com.alibaba.excel.write.metadata.holder.WriteSheetHolder writeSheetHolder, com.alibaba.excel.write.metadata.holder.WriteTableHolder writeTableHolder, List<com.alibaba.excel.metadata.CellData> cellDataList, org.apache.poi.ss.usermodel.Cell cell, com.alibaba.excel.metadata.Head head, Integer relativeRowIndex, Boolean isHead) {
|
|
|
|
|
// Title Centering (Row 0)
|
|
|
|
|
if (cell.getRowIndex() == 0 && cell.getColumnIndex() == 0) {
|
|
|
|
|
if (centerStyle == null) {
|
|
|
|
|
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
|
|
|
|
centerStyle = workbook.createCellStyle();
|
|
|
|
|
centerStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER);
|
|
|
|
|
centerStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
|
|
|
|
org.apache.poi.ss.usermodel.Font font = workbook.createFont();
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
font.setFontHeightInPoints((short) 16);
|
|
|
|
|
centerStyle.setFont(font);
|
|
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
cell.setCellStyle(coloredStyle);
|
|
|
|
|
} else if (aquaRowIndices.contains(cell.getRowIndex())) {
|
|
|
|
|
if (aquaStyle == null) {
|
|
|
|
|
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
|
|
|
|
aquaStyle = workbook.createCellStyle();
|
|
|
|
|
aquaStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.AQUA.getIndex());
|
|
|
|
|
aquaStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
|
|
|
|
aquaStyle.setWrapText(true);
|
|
|
|
|
}
|
|
|
|
|
cell.setCellStyle(aquaStyle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.sheet("报价单")
|
|
|
|
|
.doWrite(rows);
|
|
|
|
|
|
|
|
|
|
return fileName;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("导出报价单失败", e);
|
|
|
|
|
throw new ServiceException("导出报价单失败,请稍后重试");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void addSection(List<List<Object>> rows, String title, List<QuotationProductInfo> list, Set<Integer> coloredRowIndices, Set<Integer> aquaRowIndices, AtomicInteger sectionCounter) {
|
|
|
|
|
if (CollUtil.isEmpty(list)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 记录标题行索引 (Aqua)
|
|
|
|
|
aquaRowIndices.add(rows.size());
|
|
|
|
|
int currentSection = sectionCounter.getAndIncrement();
|
|
|
|
|
|
|
|
|
|
// 添加标题行 (补齐12列)
|
|
|
|
|
List<Object> titleRow = new ArrayList<>();
|
|
|
|
|
titleRow.add(currentSection);
|
|
|
|
|
titleRow.add(title);
|
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
|
titleRow.add("");
|
|
|
|
|
}
|
|
|
|
|
rows.add(titleRow);
|
|
|
|
|
|
|
|
|
|
double sumAllPrice = 0.0;
|
|
|
|
|
double sumCatalogueAllPrice = 0.0;
|
|
|
|
|
|
|
|
|
|
// 添加数据行
|
|
|
|
|
int index = 1;
|
|
|
|
|
for (QuotationProductInfo item : list) {
|
|
|
|
|
List<Object> row = new ArrayList<>();
|
|
|
|
|
row.add(currentSection + "_" + index++);
|
|
|
|
|
row.add(item.getProductBomCode());
|
|
|
|
|
row.add(item.getModel());
|
|
|
|
|
row.add(item.getProductDesc());
|
|
|
|
|
row.add(item.getQuantity());
|
|
|
|
|
row.add(item.getCataloguePrice());
|
|
|
|
|
row.add(item.getGuidanceDiscount());
|
|
|
|
|
row.add(item.getPrice());
|
|
|
|
|
row.add(item.getAllPrice());
|
|
|
|
|
row.add(item.getCatalogueAllPrice());
|
|
|
|
|
row.add(""); // CID信息
|
|
|
|
|
row.add(item.getRemark());
|
|
|
|
|
rows.add(row);
|
|
|
|
|
|
|
|
|
|
if (item.getAllPrice() != null) {
|
|
|
|
|
sumAllPrice += item.getAllPrice();
|
|
|
|
|
}
|
|
|
|
|
if (item.getCatalogueAllPrice() != null) {
|
|
|
|
|
sumCatalogueAllPrice += item.getCatalogueAllPrice();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加合计行1
|
|
|
|
|
coloredRowIndices.add(rows.size());
|
|
|
|
|
List<Object> subTotalRow1 = new ArrayList<>();
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add(title);
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add(sumAllPrice);
|
|
|
|
|
subTotalRow1.add(sumCatalogueAllPrice);
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
subTotalRow1.add("");
|
|
|
|
|
rows.add(subTotalRow1);
|
|
|
|
|
|
|
|
|
|
// 添加合计行2
|
|
|
|
|
coloredRowIndices.add(rows.size());
|
|
|
|
|
List<Object> subTotalRow2 = new ArrayList<>();
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add("配置组小计");
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add(sumAllPrice);
|
|
|
|
|
subTotalRow2.add(sumCatalogueAllPrice);
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
subTotalRow2.add("");
|
|
|
|
|
rows.add(subTotalRow2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 自定义列宽策略:自适应但有最大宽度
|
|
|
|
|
public static class CustomColumnWidthStrategy extends AbstractColumnWidthStyleStrategy {
|
|
|
|
|
private static final int MAX_COLUMN_WIDTH = 50;
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
|
|
|
|
|
if (isHead != null && isHead) {
|
|
|
|
|
// 如果是表头,不需特别处理,EasyExcel会自动处理,或者这里也可以计算
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (cellDataList != null && !cellDataList.isEmpty()) {
|
|
|
|
|
CellData cellData = cellDataList.get(0);
|
|
|
|
|
if (cellData.getType() == com.alibaba.excel.enums.CellDataTypeEnum.STRING) {
|
|
|
|
|
String stringValue = cellData.getStringValue();
|
|
|
|
|
if (StrUtil.isNotEmpty(stringValue)) {
|
|
|
|
|
int length = stringValue.getBytes().length;
|
|
|
|
|
int width = Math.min(length + 2, MAX_COLUMN_WIDTH);
|
|
|
|
|
// 当前列宽
|
|
|
|
|
int currentColumnWidth = writeSheetHolder.getSheet().getColumnWidth(cell.getColumnIndex()) / 256;
|
|
|
|
|
if (width > currentColumnWidth) {
|
|
|
|
|
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), width * 256);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|