|
@@ -16,10 +16,16 @@ import org.apache.commons.lang.StringUtils;
|
|
|
import org.apache.ibatis.session.ExecutorType;
|
|
|
import org.apache.ibatis.session.SqlSession;
|
|
|
import org.apache.ibatis.session.SqlSessionFactory;
|
|
|
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
|
+import org.apache.poi.ss.usermodel.*;
|
|
|
import org.apache.poi.ss.usermodel.Cell;
|
|
|
import org.apache.poi.ss.usermodel.Row;
|
|
|
import org.apache.poi.ss.usermodel.Sheet;
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
|
|
+import org.apache.poi.ss.util.WorkbookUtil;
|
|
|
+import org.apache.poi.xssf.usermodel.XSSFCell;
|
|
|
+import org.apache.poi.xssf.usermodel.XSSFRow;
|
|
|
+import org.apache.poi.xssf.usermodel.XSSFSheet;
|
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
|
|
import org.jsoup.Jsoup;
|
|
|
import org.jsoup.nodes.Document;
|
|
@@ -45,7 +51,6 @@ import org.springblade.core.log.exception.ServiceException;
|
|
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
|
|
import org.springblade.core.redis.cache.BladeRedis;
|
|
|
import org.springblade.core.secure.utils.AuthUtil;
|
|
|
-import org.springblade.core.secure.utils.SecureUtil;
|
|
|
import org.springblade.core.tool.api.R;
|
|
|
import org.springblade.core.tool.node.ForestNodeMerger;
|
|
|
import org.springblade.core.tool.utils.*;
|
|
@@ -56,15 +61,19 @@ import org.springblade.manager.excel.WbsExcelBatchUtil;
|
|
|
import org.springblade.manager.excel.WbsExcelUtil;
|
|
|
import org.springblade.manager.feign.ContractClient;
|
|
|
import org.springblade.manager.mapper.*;
|
|
|
+import org.springblade.manager.service.INodeBaseInfoService;
|
|
|
import org.springblade.manager.service.ITableFileService;
|
|
|
import org.springblade.manager.service.IWbsTreeContractService;
|
|
|
import org.springblade.manager.utils.CompositeKey;
|
|
|
import org.springblade.manager.vo.*;
|
|
|
import org.springblade.system.cache.ParamCache;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.context.annotation.Lazy;
|
|
|
-import org.springframework.dao.DataAccessException;
|
|
|
+import org.springframework.core.io.ByteArrayResource;
|
|
|
+import org.springframework.core.io.Resource;
|
|
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
import org.springframework.jdbc.BadSqlGrammarException;
|
|
|
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
@@ -77,10 +86,8 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
-import java.io.File;
|
|
|
-import java.io.FileInputStream;
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.InputStream;
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.io.*;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.math.BigInteger;
|
|
|
import java.math.MathContext;
|
|
@@ -134,6 +141,8 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
|
|
|
private final Map<String, List<WbsTreeContractLazyVO>> localCacheParentCountNodes = new ConcurrentHashMap<>();
|
|
|
@Autowired
|
|
|
private WbsTreeMapper wbsTreeMapper;
|
|
|
+ @Autowired
|
|
|
+ private WbsTreeContractMapper wbsTreeContractMapper;
|
|
|
|
|
|
@Override
|
|
|
public List<WbsTreeContract> selectQueryCurrentNodeByAncestors(List<String> ids, String contractId) {
|
|
@@ -614,23 +623,6 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
|
|
|
// if(info!=null){
|
|
|
// resultTabs.forEach(tab->tab.setFileName(info.getName()));
|
|
|
// }
|
|
|
- //同步标识
|
|
|
- if (CollectionUtil.isNotEmpty(resultTabs) && SecureUtil.isAdministrator()) {
|
|
|
- List<Long> collect1 = resultTabs.stream().map(WbsTreeContract::getPKeyId).collect(Collectors.toList());
|
|
|
- List<Long> longs = null;
|
|
|
- try {
|
|
|
- longs = jdbcTemplate.queryForList("select p_key_id from m_wbs_tree_contract_extend where is_sync = 1 and is_deleted = 0 and p_key_id in (" + StringUtils.join(collect1, ",") + ")", Long.class);
|
|
|
- } catch (DataAccessException e) {
|
|
|
- //TODO 暂时忽略异常,避免表不存在报错
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- if (CollectionUtil.isNotEmpty(longs)) {
|
|
|
- List<Long> finalLongs = longs;
|
|
|
- resultTabs.forEach(f -> {
|
|
|
- f.setIsSync(finalLongs.contains(f.getPKeyId()) && f.getIsBussShow() != 2 ? 1 : 0);
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
if (Optional.ofNullable(wbsTreeContract.getIsUseSort()).orElse(0) == 0) {
|
|
|
//表单排序
|
|
@@ -3243,7 +3235,6 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* 展开所有合并单元格并将值填充到每个单元格
|
|
|
*
|
|
@@ -4468,6 +4459,766 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
|
|
|
return list;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public ResponseEntity<Resource> exportTree(Long contractId, HttpServletResponse response) throws IOException, InvalidFormatException {
|
|
|
+
|
|
|
+ String templatePath = "/mnt/sdc/Users/hongchuangyanfa/Desktop/excel/gcdcTemplate.xlsx";
|
|
|
+ //String templatePath = "C:\\upload\\excel\\gcdc.xlsx";
|
|
|
+ // 查询数据
|
|
|
+ String sql = "select * from m_wbs_tree_contract where contract_id = " + contractId +
|
|
|
+ " and is_deleted = 0 and type = 1 and node_type != 6 and p_id != 0";
|
|
|
+ List<WbsTreeContract> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(WbsTreeContract.class));
|
|
|
+
|
|
|
+ InputStream templateStream = new FileInputStream(new File(templatePath));
|
|
|
+ org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(templateStream);
|
|
|
+ Sheet templateSheet = workbook.getSheetAt(0);
|
|
|
+
|
|
|
+ // 移除默认的Sheet1
|
|
|
+ workbook.removeSheetAt(0);
|
|
|
+
|
|
|
+ // 创建居中的单元格样式
|
|
|
+ CellStyle centerStyle = workbook.createCellStyle();
|
|
|
+ centerStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ centerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+
|
|
|
+ // 按单位工程分组
|
|
|
+ Map<Long, List<WbsTreeContract>> unitProjectMap = list.stream()
|
|
|
+ .filter(item -> item.getNodeType() == 18) // 单位工程
|
|
|
+ .collect(Collectors.toMap(
|
|
|
+ WbsTreeContract::getPKeyId,
|
|
|
+ unit -> list.stream()
|
|
|
+ .filter(item -> item.getAncestors() != null &&
|
|
|
+ item.getAncestors().contains(unit.getPKeyId().toString()))
|
|
|
+ .collect(Collectors.toList())
|
|
|
+ ));
|
|
|
+
|
|
|
+ // 为每个单位工程创建sheet
|
|
|
+ for (Map.Entry<Long, List<WbsTreeContract>> entry : unitProjectMap.entrySet()) {
|
|
|
+ Long unitId = entry.getKey();
|
|
|
+ List<WbsTreeContract> unitProjects = entry.getValue();
|
|
|
+
|
|
|
+ // 获取单位工程名称
|
|
|
+ String unitName = unitProjects.stream()
|
|
|
+ .filter(item -> item.getPKeyId().equals(unitId))
|
|
|
+ .findFirst()
|
|
|
+ .map(WbsTreeContract::getNodeName)
|
|
|
+ .orElse("未知单位工程");
|
|
|
+ // 创建安全的sheet名称
|
|
|
+ String safeUnitName = unitName.replaceAll("[\\\\/*\\[\\]:?]", "_");
|
|
|
+ if (safeUnitName.length() > 31) {
|
|
|
+ safeUnitName = safeUnitName.substring(0, 31);
|
|
|
+ }
|
|
|
+ String validSheetName = WorkbookUtil.createSafeSheetName(safeUnitName);
|
|
|
+
|
|
|
+ // 创建sheet并设置名称
|
|
|
+ Sheet sheet = workbook.createSheet(validSheetName);
|
|
|
+
|
|
|
+ // 创建表头(两行)- 使用模板sheet
|
|
|
+ createHeaderRows(sheet, templateSheet, centerStyle);
|
|
|
+
|
|
|
+ // 处理数据行
|
|
|
+ processDataRows(sheet, unitProjects, unitId, centerStyle);
|
|
|
+
|
|
|
+ // 设置列宽自动调整
|
|
|
+ autoSizeColumns(sheet);
|
|
|
+
|
|
|
+ // 只隐藏pkeyId列(不保护,允许用户取消隐藏)
|
|
|
+ hidePkeyIdColumnsOnly(sheet);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存文件到本地(本地测试放开,正式环境不需要)
|
|
|
+ // String outputPath = "C:\\upload\\excel\\111.xlsx";
|
|
|
+ //saveWorkbookToFile(workbook, outputPath);
|
|
|
+
|
|
|
+ // 同时返回给浏览器下载
|
|
|
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
|
+ workbook.write(outputStream);
|
|
|
+ workbook.close();
|
|
|
+
|
|
|
+ ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray());
|
|
|
+ return ResponseEntity.ok()
|
|
|
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=划分.xlsx")
|
|
|
+ .contentType(MediaType.APPLICATION_OCTET_STREAM)
|
|
|
+ .contentLength(resource.contentLength())
|
|
|
+ .body(resource);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 列映射关系:pkeyId列 -> 划分编码列
|
|
|
+ private static final Map<Integer, Integer> COLUMN_MAPPING = new HashMap<>();
|
|
|
+
|
|
|
+ static {
|
|
|
+ COLUMN_MAPPING.put(2, 0); // C列(2) -> A列(0) 单位工程
|
|
|
+ COLUMN_MAPPING.put(5, 3); // F列(5) -> D列(3) 分部工程
|
|
|
+ COLUMN_MAPPING.put(8, 6); // I列(8) -> G列(6) 子分部工程
|
|
|
+ COLUMN_MAPPING.put(11, 9); // L列(11) -> J列(9) 分项工程
|
|
|
+ COLUMN_MAPPING.put(14, 12); // O列(14) -> M列(12) 子分项工程
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R importPartitionCode(MultipartFile file) {
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ return R.fail("文件不能为空");
|
|
|
+ }
|
|
|
+ String originalFilename = file.getOriginalFilename();
|
|
|
+ if (originalFilename == null || !originalFilename.endsWith(".xlsx")) {
|
|
|
+ return R.fail("只支持.xlsx格式的文件");
|
|
|
+ }
|
|
|
+ List<WbsTreeContract> wbsTreeContractList = new ArrayList<>();
|
|
|
+ int totalCount = 0;
|
|
|
+ int successCount = 0;
|
|
|
+ try (InputStream inputStream = file.getInputStream();
|
|
|
+ XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) {
|
|
|
+
|
|
|
+ // 遍历所有sheet
|
|
|
+ for (int sheetIndex = 0; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) {
|
|
|
+ XSSFSheet sheet = workbook.getSheetAt(sheetIndex);
|
|
|
+ // 跳过表头(前两行)
|
|
|
+ for (int rowIndex = 2; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
+ XSSFRow row = sheet.getRow(rowIndex);
|
|
|
+ if (row == null) continue;
|
|
|
+
|
|
|
+ // 处理每一列的映射关系
|
|
|
+ for (Map.Entry<Integer, Integer> entry : COLUMN_MAPPING.entrySet()) {
|
|
|
+ int pkeyColIndex = entry.getKey();
|
|
|
+ int codeColIndex = entry.getValue();
|
|
|
+
|
|
|
+ XSSFCell pkeyCell = row.getCell(pkeyColIndex);
|
|
|
+ XSSFCell codeCell = row.getCell(codeColIndex);
|
|
|
+
|
|
|
+ // 检查pkeyId和划分编码是否有效
|
|
|
+ if (isValidCell(pkeyCell) && isValidCell(codeCell)) {
|
|
|
+ String pkeyId = getCellValue(pkeyCell);
|
|
|
+ String partitionCode = getCellValue(codeCell);
|
|
|
+
|
|
|
+ if (StringUtils.isNotBlank(pkeyId) && StringUtils.isNotBlank(partitionCode)) {
|
|
|
+ totalCount++;
|
|
|
+ WbsTreeContract wbsTreeContract = new WbsTreeContract();
|
|
|
+ wbsTreeContract.setPKeyId(Long.parseLong(pkeyId.trim()));
|
|
|
+ wbsTreeContract.setPartitionCode(partitionCode.trim());
|
|
|
+ wbsTreeContractList.add(wbsTreeContract);
|
|
|
+ successCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量保存到数据库
|
|
|
+ if (!wbsTreeContractList.isEmpty()) {
|
|
|
+ for (WbsTreeContract wbsTreeContract : wbsTreeContractList) {
|
|
|
+ wbsTreeContractMapper.updatePartitionCodeByPKyId(wbsTreeContract);
|
|
|
+ }
|
|
|
+ return R.status(true);
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("读取Excel文件失败", e);
|
|
|
+ return R.fail("读取文件失败: " + e.getMessage());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("导入划分编码数据失败", e);
|
|
|
+ return R.fail("导入失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ return R.status(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查单元格是否有效
|
|
|
+ */
|
|
|
+ private boolean isValidCell(XSSFCell cell) {
|
|
|
+ return cell != null && cell.getCellType() != CellType.BLANK.getCode();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取单元格的值
|
|
|
+ */
|
|
|
+ private String getCellValue(XSSFCell cell) {
|
|
|
+ if (cell == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ return cell.getStringCellValue();
|
|
|
+ } catch (Exception e) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 只隐藏pkeyId列(不设置保护,允许用户取消隐藏)
|
|
|
+ private void hidePkeyIdColumnsOnly(Sheet sheet) {
|
|
|
+ int[] pkeyIdColumns = {2, 5, 8, 11, 14};
|
|
|
+
|
|
|
+ // 只隐藏列,不设置任何保护
|
|
|
+ for (int col : pkeyIdColumns) {
|
|
|
+ sheet.setColumnHidden(col, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 特别注意:不调用 sheet.protectSheet() 方法
|
|
|
+ // 这样用户就可以自由调整列宽和取消隐藏列
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建表头行(两行)- 添加居中样式
|
|
|
+ private void createHeaderRows(Sheet sheet, Sheet templateSheet, CellStyle centerStyle) {
|
|
|
+ // 复制第一行
|
|
|
+ Row templateHeaderRow = templateSheet.getRow(0);
|
|
|
+ Row headerRow = sheet.createRow(0);
|
|
|
+ for (int i = 0; i < 15; i++) {
|
|
|
+ Cell templateCell = templateHeaderRow.getCell(i);
|
|
|
+ Cell newCell = headerRow.createCell(i);
|
|
|
+ if (templateCell != null) {
|
|
|
+ newCell.setCellValue(templateCell.getStringCellValue());
|
|
|
+ newCell.setCellStyle(centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 复制第二行
|
|
|
+ Row templateSubHeaderRow = templateSheet.getRow(1);
|
|
|
+ Row subHeaderRow = sheet.createRow(1);
|
|
|
+ for (int i = 0; i < 15; i++) {
|
|
|
+ Cell templateCell = templateSubHeaderRow.getCell(i);
|
|
|
+ Cell newCell = subHeaderRow.createCell(i);
|
|
|
+ if (templateCell != null) {
|
|
|
+ newCell.setCellValue(templateCell.getStringCellValue());
|
|
|
+ newCell.setCellStyle(centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从模板复制合并区域
|
|
|
+ copyMergedRegionsFromTemplate(sheet, templateSheet);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理数据行 - 添加居中样式参数
|
|
|
+ private void processDataRows(Sheet sheet, List<WbsTreeContract> unitProjects, Long unitId, CellStyle centerStyle) {
|
|
|
+ int rowNum = 2; // 从第3行开始(0-based,所以是第3行)
|
|
|
+ Map<Integer, List<CellRangeAddress>> mergeMap = new HashMap<>();
|
|
|
+
|
|
|
+ // 按照层级顺序查找叶子节点:子分项 -> 分项 -> 子分部 -> 分部 -> 单位工程
|
|
|
+ List<WbsTreeContract> leafNodes = findLeafNodesByPriority(unitProjects);
|
|
|
+
|
|
|
+ for (WbsTreeContract leafNode : leafNodes) {
|
|
|
+ Row row = sheet.createRow(rowNum);
|
|
|
+
|
|
|
+ // 解析祖级节点
|
|
|
+ String[] ancestors = leafNode.getAncestors().split(",");
|
|
|
+
|
|
|
+ // 转换为 List<Long>
|
|
|
+ List<Long> ancestorIds = Arrays.stream(ancestors)
|
|
|
+ .map(Long::parseLong)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 根据叶子节点类型填充各级工程数据
|
|
|
+ fillEngineeringDataByLeafType(row, unitProjects, ancestorIds, mergeMap, rowNum, centerStyle, leafNode.getNodeType());
|
|
|
+
|
|
|
+ rowNum++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 应用合并单元格
|
|
|
+ applyMergedRegions(sheet, mergeMap, centerStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按照优先级查找叶子节点
|
|
|
+ private List<WbsTreeContract> findLeafNodesByPriority(List<WbsTreeContract> projects) {
|
|
|
+ // 按照节点类型优先级:子分项(5) -> 分项(4) -> 子分部(3) -> 分部(2) -> 单位工程(18)
|
|
|
+ List<WbsTreeContract> leafNodes = new ArrayList<>();
|
|
|
+
|
|
|
+ // 1. 先找子分项工程 (节点类型5)
|
|
|
+ leafNodes = projects.stream()
|
|
|
+ .filter(item -> item.getNodeType() == 5)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ if (!leafNodes.isEmpty()) {
|
|
|
+ return leafNodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 如果没有子分项,找分项工程 (节点类型4)
|
|
|
+ leafNodes = projects.stream()
|
|
|
+ .filter(item -> item.getNodeType() == 4)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ if (!leafNodes.isEmpty()) {
|
|
|
+ return leafNodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 如果没有分项,找子分部工程 (节点类型3)
|
|
|
+ leafNodes = projects.stream()
|
|
|
+ .filter(item -> item.getNodeType() == 3)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ if (!leafNodes.isEmpty()) {
|
|
|
+ return leafNodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 如果没有子分部,找分部工程 (节点类型2)
|
|
|
+ leafNodes = projects.stream()
|
|
|
+ .filter(item -> item.getNodeType() == 2)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ if (!leafNodes.isEmpty()) {
|
|
|
+ return leafNodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 最后找单位工程 (节点类型18)
|
|
|
+ return projects.stream()
|
|
|
+ .filter(item -> item.getNodeType() == 18)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据叶子节点类型填充工程数据
|
|
|
+ private void fillEngineeringDataByLeafType(Row row, List<WbsTreeContract> projects, List<Long> ancestorIds,
|
|
|
+ Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum,
|
|
|
+ CellStyle centerStyle, int leafNodeType) {
|
|
|
+
|
|
|
+ switch (leafNodeType) {
|
|
|
+ case 5: // 子分项工程 - 显示所有层级
|
|
|
+ fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
|
|
|
+ fillLevelData(row, projects, ancestorIds, 3, 5, mergeMap, rowNum, 2, centerStyle); // 分部工程
|
|
|
+ fillLevelData(row, projects, ancestorIds, 6, 8, mergeMap, rowNum, 3, centerStyle); // 子分部工程
|
|
|
+ fillLevelData(row, projects, ancestorIds, 9, 11, mergeMap, rowNum, 4, centerStyle); // 分项工程
|
|
|
+ fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 12, 13, 14, mergeMap, rowNum, 5, centerStyle); // 子分项工程
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 4: // 分项工程 - 显示到分项
|
|
|
+ fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
|
|
|
+ fillLevelData(row, projects, ancestorIds, 3, 5, mergeMap, rowNum, 2, centerStyle); // 分部工程
|
|
|
+ fillLevelData(row, projects, ancestorIds, 6, 8, mergeMap, rowNum, 3, centerStyle); // 子分部工程
|
|
|
+ fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 9, 10, 11, mergeMap, rowNum, 4, centerStyle); // 分项工程
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 3: // 子分部工程 - 显示到子分部
|
|
|
+ fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
|
|
|
+ fillLevelData(row, projects, ancestorIds, 3, 5, mergeMap, rowNum, 2, centerStyle); // 分部工程
|
|
|
+ fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 6, 7, 8, mergeMap, rowNum, 3, centerStyle); // 子分部工程
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 2: // 分部工程 - 显示到分部
|
|
|
+ fillLevelData(row, projects, ancestorIds, 0, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
|
|
|
+ fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 3, 4, 5, mergeMap, rowNum, 2, centerStyle); // 分部工程
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 18: // 单位工程 - 只显示单位工程
|
|
|
+ fillLeafNodeData(row, projects, ancestorIds.get(ancestorIds.size() - 1), 0, 1, 2, mergeMap, rowNum, 18, centerStyle); // 单位工程
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ // 其他情况,尝试填充所有能找到的层级
|
|
|
+ fillAllAvailableLevels(row, projects, ancestorIds, mergeMap, rowNum, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充叶子节点数据
|
|
|
+ private void fillLeafNodeData(Row row, List<WbsTreeContract> nodes, Long nodeId,
|
|
|
+ int codeCol, int nameCol, int idCol,
|
|
|
+ Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, int expectedType, CellStyle centerStyle) {
|
|
|
+ WbsTreeContract node = findNodeById(nodes, nodeId);
|
|
|
+ if (node != null && node.getNodeType() == expectedType) {
|
|
|
+ // 划分编码(如果有)
|
|
|
+ if (node.getPartitionCode() != null && !node.getPartitionCode().isEmpty()) {
|
|
|
+ setCellValue(row, codeCol, node.getPartitionCode(), mergeMap, rowNum, centerStyle);
|
|
|
+ }
|
|
|
+ // 名称(一定有)
|
|
|
+ setCellValue(row, nameCol, node.getNodeName(), mergeMap, rowNum, centerStyle);
|
|
|
+ // pkeyId
|
|
|
+ setCellValue(row, idCol, node.getPKeyId().toString(), mergeMap, rowNum, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充所有能找到的层级
|
|
|
+ private void fillAllAvailableLevels(Row row, List<WbsTreeContract> nodes, List<Long> ancestorIds,
|
|
|
+ Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, CellStyle centerStyle) {
|
|
|
+ // 尝试填充单位工程
|
|
|
+ if (ancestorIds.size() > 0) {
|
|
|
+ WbsTreeContract unit = findNodeByIdAndType(nodes, ancestorIds, 18);
|
|
|
+ if (unit != null) {
|
|
|
+ fillLeafNodeData(row, nodes, unit.getPKeyId(), 0, 1, 2, mergeMap, rowNum, 18, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试填充分部工程
|
|
|
+ if (ancestorIds.size() > 1) {
|
|
|
+ WbsTreeContract division = findNodeByIdAndType(nodes, ancestorIds, 2);
|
|
|
+ if (division != null) {
|
|
|
+ fillLeafNodeData(row, nodes, division.getPKeyId(), 3, 4, 5, mergeMap, rowNum, 2, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试填充子分部工程
|
|
|
+ if (ancestorIds.size() > 2) {
|
|
|
+ WbsTreeContract subDivision = findNodeByIdAndType(nodes, ancestorIds, 3);
|
|
|
+ if (subDivision != null) {
|
|
|
+ fillLeafNodeData(row, nodes, subDivision.getPKeyId(), 6, 7, 8, mergeMap, rowNum, 3, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试填充分项工程
|
|
|
+ if (ancestorIds.size() > 3) {
|
|
|
+ WbsTreeContract item = findNodeByIdAndType(nodes, ancestorIds, 4);
|
|
|
+ if (item != null) {
|
|
|
+ fillLeafNodeData(row, nodes, item.getPKeyId(), 9, 10, 11, mergeMap, rowNum, 4, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试填充子分项工程
|
|
|
+ if (ancestorIds.size() > 4) {
|
|
|
+ WbsTreeContract subItem = findNodeByIdAndType(nodes, ancestorIds, 5);
|
|
|
+ if (subItem != null) {
|
|
|
+ fillLeafNodeData(row, nodes, subItem.getPKeyId(), 12, 13, 14, mergeMap, rowNum, 5, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据ID查找节点
|
|
|
+ private WbsTreeContract findNodeById(List<WbsTreeContract> list, Long id) {
|
|
|
+ return list.stream()
|
|
|
+ .filter(item -> item.getPKeyId().equals(id))
|
|
|
+ .findFirst()
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充各级工程数据 - 添加居中样式参数
|
|
|
+ private void fillLevelData(Row row, List<WbsTreeContract> projects, List<Long> ancestors,
|
|
|
+ int startCol, int endCol,
|
|
|
+ Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, int nodeType, CellStyle centerStyle) {
|
|
|
+ if (!ancestors.isEmpty()) {
|
|
|
+ WbsTreeContract node = findNodeByIdAndType(projects, ancestors, nodeType);
|
|
|
+ if (node != null) {
|
|
|
+ // 划分编码(可能为null)
|
|
|
+ if (node.getPartitionCode() != null && !node.getPartitionCode().isEmpty()) {
|
|
|
+ setCellValue(row, startCol, node.getPartitionCode(), mergeMap, rowNum, centerStyle);
|
|
|
+ }
|
|
|
+ // 名称
|
|
|
+ setCellValue(row, startCol + 1, node.getNodeName(), mergeMap, rowNum, centerStyle);
|
|
|
+ // pkeyId
|
|
|
+ setCellValue(row, endCol, node.getPKeyId().toString(), mergeMap, rowNum, centerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 设置单元格值并记录合并信息 - 添加居中样式参数
|
|
|
+ private void setCellValue(Row row, int col, String value, Map<Integer, List<CellRangeAddress>> mergeMap, int rowNum, CellStyle centerStyle) {
|
|
|
+ Cell cell = row.getCell(col);
|
|
|
+ if (cell == null) {
|
|
|
+ cell = row.createCell(col);
|
|
|
+ }
|
|
|
+ cell.setCellValue(value);
|
|
|
+ cell.setCellStyle(centerStyle); // 设置居中样式和边框
|
|
|
+
|
|
|
+ if (!mergeMap.containsKey(col)) {
|
|
|
+ mergeMap.put(col, new ArrayList<>());
|
|
|
+ }
|
|
|
+ mergeMap.get(col).add(new CellRangeAddress(rowNum, rowNum, col, col));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 应用合并单元格 - 添加居中样式参数
|
|
|
+ private void applyMergedRegions(Sheet sheet, Map<Integer, List<CellRangeAddress>> mergeMap, CellStyle centerStyle) {
|
|
|
+ // 使用Set来记录已经合并的区域,避免重复合并
|
|
|
+ Set<String> mergedRegions = new HashSet<>();
|
|
|
+
|
|
|
+ // 首先处理名称列的合并
|
|
|
+ Map<Integer, List<CellRangeAddress>> nameColumnMerges = new HashMap<>();
|
|
|
+
|
|
|
+ // 名称列的索引:1, 4, 7, 10, 13
|
|
|
+ int[] nameColumns = {1, 4, 7, 10, 13};
|
|
|
+
|
|
|
+ // 收集名称列的合并信息
|
|
|
+ for (int nameCol : nameColumns) {
|
|
|
+ if (mergeMap.containsKey(nameCol)) {
|
|
|
+ nameColumnMerges.put(nameCol, new ArrayList<>(mergeMap.get(nameCol)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并名称列和对应的划分编码列
|
|
|
+ for (Map.Entry<Integer, List<CellRangeAddress>> entry : nameColumnMerges.entrySet()) {
|
|
|
+ int nameCol = entry.getKey();
|
|
|
+ List<CellRangeAddress> regions = entry.getValue();
|
|
|
+
|
|
|
+ if (regions.size() > 1) {
|
|
|
+ // 按行号排序
|
|
|
+ regions.sort(Comparator.comparingInt(CellRangeAddress::getFirstRow));
|
|
|
+
|
|
|
+ int mergeStart = -1;
|
|
|
+ int mergeEnd = -1;
|
|
|
+ String lastValue = null;
|
|
|
+
|
|
|
+ for (CellRangeAddress region : regions) {
|
|
|
+ int rowNum = region.getFirstRow();
|
|
|
+ Row row = sheet.getRow(rowNum);
|
|
|
+ if (row == null) continue;
|
|
|
+
|
|
|
+ Cell cell = row.getCell(nameCol);
|
|
|
+ if (cell == null) continue;
|
|
|
+
|
|
|
+ String currentValue = getCellStringValue(cell);
|
|
|
+
|
|
|
+ if (lastValue == null) {
|
|
|
+ // 第一个单元格
|
|
|
+ mergeStart = rowNum;
|
|
|
+ mergeEnd = rowNum;
|
|
|
+ lastValue = currentValue;
|
|
|
+ } else if (currentValue.equals(lastValue)) {
|
|
|
+ // 值相同,扩展合并范围
|
|
|
+ mergeEnd = rowNum;
|
|
|
+ } else {
|
|
|
+ // 值不同,合并之前的区域
|
|
|
+ if (mergeStart < mergeEnd) {
|
|
|
+ mergeNameAndCodeColumns(sheet, nameCol, mergeStart, mergeEnd, centerStyle, mergedRegions);
|
|
|
+ }
|
|
|
+ // 开始新的合并区域
|
|
|
+ mergeStart = rowNum;
|
|
|
+ mergeEnd = rowNum;
|
|
|
+ lastValue = currentValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并最后一段区域
|
|
|
+ if (mergeStart < mergeEnd) {
|
|
|
+ mergeNameAndCodeColumns(sheet, nameCol, mergeStart, mergeEnd, centerStyle, mergedRegions);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理其他列的合并(pkeyId列等)
|
|
|
+ for (Map.Entry<Integer, List<CellRangeAddress>> entry : mergeMap.entrySet()) {
|
|
|
+ int col = entry.getKey();
|
|
|
+ List<CellRangeAddress> regions = entry.getValue();
|
|
|
+
|
|
|
+ // 跳过已经处理过的名称列
|
|
|
+ if (isNameColumn(col)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (regions.size() > 1) {
|
|
|
+ // 按行号排序
|
|
|
+ regions.sort(Comparator.comparingInt(CellRangeAddress::getFirstRow));
|
|
|
+
|
|
|
+ int mergeStart = -1;
|
|
|
+ int mergeEnd = -1;
|
|
|
+ String lastValue = null;
|
|
|
+
|
|
|
+ for (CellRangeAddress region : regions) {
|
|
|
+ int rowNum = region.getFirstRow();
|
|
|
+ Row row = sheet.getRow(rowNum);
|
|
|
+ if (row == null) continue;
|
|
|
+
|
|
|
+ Cell cell = row.getCell(col);
|
|
|
+ if (cell == null) continue;
|
|
|
+
|
|
|
+ String currentValue = getCellStringValue(cell);
|
|
|
+
|
|
|
+ if (lastValue == null) {
|
|
|
+ // 第一个单元格
|
|
|
+ mergeStart = rowNum;
|
|
|
+ mergeEnd = rowNum;
|
|
|
+ lastValue = currentValue;
|
|
|
+ } else if (currentValue.equals(lastValue)) {
|
|
|
+ // 值相同,扩展合并范围
|
|
|
+ mergeEnd = rowNum;
|
|
|
+ } else {
|
|
|
+ // 值不同,合并之前的区域
|
|
|
+ if (mergeStart < mergeEnd) {
|
|
|
+ mergeSingleColumnIfNotExists(sheet, col, mergeStart, mergeEnd, centerStyle, mergedRegions);
|
|
|
+ }
|
|
|
+ // 开始新的合并区域
|
|
|
+ mergeStart = rowNum;
|
|
|
+ mergeEnd = rowNum;
|
|
|
+ lastValue = currentValue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并最后一段区域
|
|
|
+ if (mergeStart < mergeEnd) {
|
|
|
+ mergeSingleColumnIfNotExists(sheet, col, mergeStart, mergeEnd, centerStyle, mergedRegions);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并名称列和对应的划分编码列(检查是否已存在)
|
|
|
+ private void mergeNameAndCodeColumns(Sheet sheet, int nameCol, int startRow, int endRow, CellStyle centerStyle, Set<String> mergedRegions) {
|
|
|
+ // 合并名称列
|
|
|
+ mergeSingleColumnIfNotExists(sheet, nameCol, startRow, endRow, centerStyle, mergedRegions);
|
|
|
+
|
|
|
+ // 合并对应的划分编码列
|
|
|
+ int codeCol = getCorrespondingCodeColumn(nameCol);
|
|
|
+ if (codeCol != -1) {
|
|
|
+ mergeSingleColumnIfNotExists(sheet, codeCol, startRow, endRow, centerStyle, mergedRegions);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并单列并设置样式(检查是否已存在)
|
|
|
+ private void mergeSingleColumnIfNotExists(Sheet sheet, int col, int startRow, int endRow, CellStyle style, Set<String> mergedRegions) {
|
|
|
+ String regionKey = col + ":" + startRow + ":" + endRow;
|
|
|
+
|
|
|
+ if (!mergedRegions.contains(regionKey)) {
|
|
|
+ CellRangeAddress mergedRegion = new CellRangeAddress(startRow, endRow, col, col);
|
|
|
+
|
|
|
+ // 检查是否已经存在相同的合并区域
|
|
|
+ boolean alreadyExists = false;
|
|
|
+ for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
|
|
|
+ CellRangeAddress existingRegion = sheet.getMergedRegion(i);
|
|
|
+ if (existingRegion.getFirstColumn() == col &&
|
|
|
+ existingRegion.getFirstRow() == startRow &&
|
|
|
+ existingRegion.getLastRow() == endRow) {
|
|
|
+ alreadyExists = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!alreadyExists) {
|
|
|
+ sheet.addMergedRegion(mergedRegion);
|
|
|
+ setMergedRegionStyle(sheet, mergedRegion, style);
|
|
|
+ mergedRegions.add(regionKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否是名称列
|
|
|
+ private boolean isNameColumn(int columnIndex) {
|
|
|
+ int[] nameColumns = {1, 4, 7, 10, 13};
|
|
|
+ for (int nameCol : nameColumns) {
|
|
|
+ if (columnIndex == nameCol) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取对应的划分编码列
|
|
|
+ private int getCorrespondingCodeColumn(int nameColumn) {
|
|
|
+ switch (nameColumn) {
|
|
|
+ case 1: return 0; // 单位工程名称(1) -> 单位工程划分编码(0)
|
|
|
+ case 4: return 3; // 分部工程名称(4) -> 分部工程划分编码(3)
|
|
|
+ case 7: return 6; // 子分部工程名称(7) -> 子分部工程划分编码(6)
|
|
|
+ case 10: return 9; // 分项工程名称(10) -> 分项工程划分编码(9)
|
|
|
+ case 13: return 12; // 子分项工程名称(13) -> 子分项工程划分编码(12)
|
|
|
+ default: return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置合并区域样式为居中
|
|
|
+ private void setMergedRegionStyle(Sheet sheet, CellRangeAddress mergedRegion, CellStyle style) {
|
|
|
+ for (int rowNum = mergedRegion.getFirstRow(); rowNum <= mergedRegion.getLastRow(); rowNum++) {
|
|
|
+ Row row = sheet.getRow(rowNum);
|
|
|
+ if (row != null) {
|
|
|
+ for (int colNum = mergedRegion.getFirstColumn(); colNum <= mergedRegion.getLastColumn(); colNum++) {
|
|
|
+ Cell cell = row.getCell(colNum);
|
|
|
+ if (cell != null) {
|
|
|
+ cell.setCellStyle(style);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取单元格的字符串值
|
|
|
+ private String getCellStringValue(Cell cell) {
|
|
|
+ if (cell == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ return cell.getStringCellValue();
|
|
|
+ } catch (Exception e) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 自动调整列宽
|
|
|
+ private void autoSizeColumns(Sheet sheet) {
|
|
|
+ for (int i = 0; i < 15; i++) {
|
|
|
+ // 先手动计算列宽
|
|
|
+ int maxWidth = calculateColumnWidth(sheet, i);
|
|
|
+
|
|
|
+ // 设置列宽,至少3000(约30个字符宽度)
|
|
|
+ int columnWidth = Math.max(maxWidth + 1000, 3000); // 加一些边距
|
|
|
+ sheet.setColumnWidth(i, columnWidth);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 手动计算列宽
|
|
|
+ private int calculateColumnWidth(Sheet sheet, int columnIndex) {
|
|
|
+ int maxWidth = 0;
|
|
|
+
|
|
|
+ for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
|
|
|
+ Row row = sheet.getRow(rowNum);
|
|
|
+ if (row != null) {
|
|
|
+ Cell cell = row.getCell(columnIndex);
|
|
|
+ if (cell != null) {
|
|
|
+ String cellValue = getCellStringValue(cell);
|
|
|
+ if (cellValue != null && !cellValue.isEmpty()) {
|
|
|
+ // 估算字符串宽度(中文字符算2个英文字符宽度)
|
|
|
+ int width = estimateStringWidth(cellValue);
|
|
|
+ maxWidth = Math.max(maxWidth, width);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return maxWidth * 256; // POI中列宽的单位是1/256个字符宽度
|
|
|
+ }
|
|
|
+
|
|
|
+ // 估算字符串宽度
|
|
|
+ private int estimateStringWidth(String text) {
|
|
|
+ if (text == null || text.isEmpty()) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ int width = 0;
|
|
|
+ for (char c : text.toCharArray()) {
|
|
|
+ // 中文字符或全角字符宽度为2,英文字符宽度为1
|
|
|
+ if (c >= 0x4E00 && c <= 0x9FA5) {
|
|
|
+ width += 2; // 中文字符
|
|
|
+ } else if (c > 0xFF00 && c < 0xFF5F) {
|
|
|
+ width += 2; // 全角字符
|
|
|
+ } else {
|
|
|
+ width += 1; // 英文字符
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return width;
|
|
|
+ }
|
|
|
+
|
|
|
+ //保存Workbook到本地文件(本地测试放开)
|
|
|
+ private void saveWorkbookToFile(org.apache.poi.ss.usermodel.Workbook workbook, String filePath) {
|
|
|
+ try {
|
|
|
+ File file = new File(filePath);
|
|
|
+ File parentDir = file.getParentFile();
|
|
|
+ if (parentDir != null && !parentDir.exists()) {
|
|
|
+ parentDir.mkdirs();
|
|
|
+ }
|
|
|
+
|
|
|
+ try (FileOutputStream fileOut = new FileOutputStream(file)) {
|
|
|
+ workbook.write(fileOut);
|
|
|
+ System.out.println("文件已成功保存到: " + filePath);
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ System.err.println("保存文件失败: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ throw new RuntimeException("保存文件失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据ID和类型查找节点
|
|
|
+ private WbsTreeContract findNodeByIdAndType(List<WbsTreeContract> list, List<Long> ids, int nodeType) {
|
|
|
+ return list.stream()
|
|
|
+ .filter(item -> ids.contains(item.getPKeyId()) && item.getNodeType() == nodeType)
|
|
|
+ .findFirst()
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 从模板复制合并区域
|
|
|
+ private void copyMergedRegionsFromTemplate(Sheet targetSheet, Sheet templateSheet) {
|
|
|
+ for (int i = 0; i < templateSheet.getNumMergedRegions(); i++) {
|
|
|
+ CellRangeAddress mergedRegion = templateSheet.getMergedRegion(i);
|
|
|
+ // 只复制前两行的合并区域
|
|
|
+ if (mergedRegion.getFirstRow() <= 1) {
|
|
|
+ targetSheet.addMergedRegion(mergedRegion);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
// 抽取公共处理逻辑
|
|
|
private void processApprovalData(APIWbsContractSubdivisionVo vo,
|
|
|
List<WbsTreeContract> children,
|