|
|
@@ -2001,4 +2001,658 @@ public class ExcelUtil {
|
|
|
}
|
|
|
return "/upload/"+fileName;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出按项目的甘特图Excel,带有水平色块显示项目时间范围
|
|
|
+ *
|
|
|
+ * @param projectDataList 项目数据列表,每个Map包含:
|
|
|
+ * projectName(项目名称), startDate(开始日期yyyy-MM-dd), endDate(结束日期yyyy-MM-dd)
|
|
|
+ * @param rangeStart 导出日期范围开始(yyyy-MM-dd)
|
|
|
+ * @param rangeEnd 导出日期范围结束(yyyy-MM-dd)
|
|
|
+ * @param path 文件保存路径
|
|
|
+ * @param fileName 文件名(不含扩展名)
|
|
|
+ * @return 文件相对路径
|
|
|
+ */
|
|
|
+ public static String exportGanttChartByProject(List<Map<String, Object>> projectDataList,
|
|
|
+ String rangeStart, String rangeEnd,
|
|
|
+ String path, String fileName) {
|
|
|
+ String result = "";
|
|
|
+ try {
|
|
|
+ java.time.LocalDate startLocalDate = java.time.LocalDate.parse(rangeStart);
|
|
|
+ java.time.LocalDate endLocalDate = java.time.LocalDate.parse(rangeEnd);
|
|
|
+
|
|
|
+ // 计算日期范围内的所有日期
|
|
|
+ List<java.time.LocalDate> dateList = new ArrayList<>();
|
|
|
+ java.time.LocalDate cur = startLocalDate;
|
|
|
+ while (!cur.isAfter(endLocalDate)) {
|
|
|
+ dateList.add(cur);
|
|
|
+ cur = cur.plusDays(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFWorkbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook();
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFSheet sheet = workbook.createSheet("甘特图");
|
|
|
+
|
|
|
+ // 设置列宽
|
|
|
+ sheet.setColumnWidth(0, 1800); // 序号
|
|
|
+ sheet.setColumnWidth(1, 6000); // 项目名称
|
|
|
+ sheet.setColumnWidth(2, 3200); // 开始时间
|
|
|
+ sheet.setColumnWidth(3, 3200); // 结束时间
|
|
|
+ for (int i = 0; i < dateList.size(); i++) {
|
|
|
+ sheet.setColumnWidth(4 + i, 1000); // 每天一列,较窄
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建样式:表头样式
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle headerStyle = workbook.createCellStyle();
|
|
|
+ headerStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)68, (byte)114, (byte)196}, null));
|
|
|
+ headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFFont headerFont = workbook.createFont();
|
|
|
+ headerFont.setColor(new XSSFColor(new byte[]{(byte)255, (byte)255, (byte)255}, null));
|
|
|
+ headerFont.setBold(true);
|
|
|
+ headerStyle.setFont(headerFont);
|
|
|
+ headerStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ headerStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 创建样式:普通单元格样式
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle normalStyle = workbook.createCellStyle();
|
|
|
+ normalStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ normalStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ normalStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 创建样式:甘特色块样式(蓝色)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle ganttStyle = workbook.createCellStyle();
|
|
|
+ ganttStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)68, (byte)114, (byte)196}, null));
|
|
|
+ ganttStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ ganttStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ ganttStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ ganttStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ ganttStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 创建样式:空白日期单元格(浅灰色背景)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle emptyDateStyle = workbook.createCellStyle();
|
|
|
+ emptyDateStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)242, (byte)242, (byte)242}, null));
|
|
|
+ emptyDateStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ emptyDateStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 创建样式:日期表头(周末用不同颜色)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle weekendHeaderStyle = workbook.createCellStyle();
|
|
|
+ weekendHeaderStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)255, (byte)192, (byte)0}, null));
|
|
|
+ weekendHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFFont weekendFont = workbook.createFont();
|
|
|
+ weekendFont.setBold(true);
|
|
|
+ weekendHeaderStyle.setFont(weekendFont);
|
|
|
+ weekendHeaderStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ weekendHeaderStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ weekendHeaderStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ weekendHeaderStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ weekendHeaderStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ weekendHeaderStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 创建样式:周末空白单元格(浅黄色)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle weekendEmptyStyle = workbook.createCellStyle();
|
|
|
+ weekendEmptyStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)255, (byte)235, (byte)156}, null));
|
|
|
+ weekendEmptyStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ weekendEmptyStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ weekendEmptyStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ weekendEmptyStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ weekendEmptyStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 第0行:表头
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFRow headerRow = sheet.createRow(0);
|
|
|
+ headerRow.setHeightInPoints(20);
|
|
|
+
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell seqCell = headerRow.createCell(0);
|
|
|
+ seqCell.setCellValue("序号");
|
|
|
+ seqCell.setCellStyle(headerStyle);
|
|
|
+
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell nameCell = headerRow.createCell(1);
|
|
|
+ nameCell.setCellValue("项目");
|
|
|
+ nameCell.setCellStyle(headerStyle);
|
|
|
+
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell startCell = headerRow.createCell(2);
|
|
|
+ startCell.setCellValue("开始时间");
|
|
|
+ startCell.setCellStyle(headerStyle);
|
|
|
+
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell endCell = headerRow.createCell(3);
|
|
|
+ endCell.setCellValue("结束时间");
|
|
|
+ endCell.setCellStyle(headerStyle);
|
|
|
+
|
|
|
+ // 日期列表头
|
|
|
+ java.time.format.DateTimeFormatter dayFormatter = java.time.format.DateTimeFormatter.ofPattern("MM/dd");
|
|
|
+ for (int i = 0; i < dateList.size(); i++) {
|
|
|
+ java.time.LocalDate d = dateList.get(i);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell dateCell = headerRow.createCell(4 + i);
|
|
|
+ dateCell.setCellValue(d.format(dayFormatter));
|
|
|
+ // 周末用不同颜色
|
|
|
+ java.time.DayOfWeek dow = d.getDayOfWeek();
|
|
|
+ if (dow == java.time.DayOfWeek.SATURDAY || dow == java.time.DayOfWeek.SUNDAY) {
|
|
|
+ dateCell.setCellStyle(weekendHeaderStyle);
|
|
|
+ } else {
|
|
|
+ dateCell.setCellStyle(headerStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 数据行
|
|
|
+ int seq = 1;
|
|
|
+ // 用于区分不同项目的颜色(多种蓝色系)
|
|
|
+ byte[][] ganttColors = {
|
|
|
+ {(byte)68, (byte)114, (byte)196},
|
|
|
+ {(byte)70, (byte)130, (byte)180},
|
|
|
+ {(byte)30, (byte)144, (byte)255},
|
|
|
+ {(byte)0, (byte)120, (byte)215},
|
|
|
+ {(byte)0, (byte)176, (byte)240},
|
|
|
+ {(byte)0, (byte)112, (byte)192},
|
|
|
+ {(byte)91, (byte)155, (byte)213},
|
|
|
+ {(byte)31, (byte)73, (byte)125},
|
|
|
+ };
|
|
|
+
|
|
|
+ // 去重:按项目名称+开始时间+结束时间去重
|
|
|
+ List<Map<String, Object>> uniqueProjects = new ArrayList<>();
|
|
|
+ Set<String> seen = new LinkedHashSet<>();
|
|
|
+ for (Map<String, Object> item : projectDataList) {
|
|
|
+ String key = (item.get("projectName") == null ? "" : item.get("projectName").toString())
|
|
|
+ + "|" + (item.get("startDate") == null ? "" : item.get("startDate").toString())
|
|
|
+ + "|" + (item.get("endDate") == null ? "" : item.get("endDate").toString());
|
|
|
+ if (seen.add(key)) {
|
|
|
+ uniqueProjects.add(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int rowIdx = 0; rowIdx < uniqueProjects.size(); rowIdx++) {
|
|
|
+ Map<String, Object> projectData = uniqueProjects.get(rowIdx);
|
|
|
+ String projectName = projectData.get("projectName") == null ? "" : projectData.get("projectName").toString();
|
|
|
+ String projStart = projectData.get("startDate") == null ? "" : projectData.get("startDate").toString();
|
|
|
+ String projEnd = projectData.get("endDate") == null ? "" : projectData.get("endDate").toString();
|
|
|
+
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFRow dataRow = sheet.createRow(rowIdx + 1);
|
|
|
+ dataRow.setHeightInPoints(18);
|
|
|
+
|
|
|
+ // 序号
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell seqDataCell = dataRow.createCell(0);
|
|
|
+ seqDataCell.setCellValue(seq++);
|
|
|
+ seqDataCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 项目名称
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell nameDataCell = dataRow.createCell(1);
|
|
|
+ nameDataCell.setCellValue(projectName);
|
|
|
+ nameDataCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 开始时间
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell startDataCell = dataRow.createCell(2);
|
|
|
+ startDataCell.setCellValue(projStart);
|
|
|
+ startDataCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 结束时间
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell endDataCell = dataRow.createCell(3);
|
|
|
+ endDataCell.setCellValue(projEnd);
|
|
|
+ endDataCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 解析项目的开始和结束日期
|
|
|
+ java.time.LocalDate projStartDate = null;
|
|
|
+ java.time.LocalDate projEndDate = null;
|
|
|
+ try {
|
|
|
+ if (!projStart.isEmpty()) projStartDate = java.time.LocalDate.parse(projStart);
|
|
|
+ if (!projEnd.isEmpty()) projEndDate = java.time.LocalDate.parse(projEnd);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ // 日期解析失败,跳过色块
|
|
|
+ }
|
|
|
+
|
|
|
+ // 选择颜色(循环使用)
|
|
|
+ byte[] colorBytes = ganttColors[rowIdx % ganttColors.length];
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle thisGanttStyle = workbook.createCellStyle();
|
|
|
+ thisGanttStyle.setFillForegroundColor(new XSSFColor(colorBytes, null));
|
|
|
+ thisGanttStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ thisGanttStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ thisGanttStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ thisGanttStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ thisGanttStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 填充日期列
|
|
|
+ for (int i = 0; i < dateList.size(); i++) {
|
|
|
+ java.time.LocalDate d = dateList.get(i);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell dateDataCell = dataRow.createCell(4 + i);
|
|
|
+ dateDataCell.setCellValue("");
|
|
|
+
|
|
|
+ boolean inRange = projStartDate != null && projEndDate != null
|
|
|
+ && !d.isBefore(projStartDate) && !d.isAfter(projEndDate);
|
|
|
+
|
|
|
+ java.time.DayOfWeek dow = d.getDayOfWeek();
|
|
|
+ boolean isWeekend = (dow == java.time.DayOfWeek.SATURDAY || dow == java.time.DayOfWeek.SUNDAY);
|
|
|
+
|
|
|
+ if (inRange) {
|
|
|
+ dateDataCell.setCellStyle(thisGanttStyle);
|
|
|
+ } else if (isWeekend) {
|
|
|
+ dateDataCell.setCellStyle(weekendEmptyStyle);
|
|
|
+ } else {
|
|
|
+ dateDataCell.setCellStyle(emptyDateStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存文件
|
|
|
+ fileName = fileName + ".xlsx";
|
|
|
+ File dir = new File(path);
|
|
|
+ if (!dir.exists()) {
|
|
|
+ dir.mkdirs();
|
|
|
+ }
|
|
|
+ FileOutputStream os = new FileOutputStream(path + fileName);
|
|
|
+ workbook.write(os);
|
|
|
+ os.flush();
|
|
|
+ os.close();
|
|
|
+ workbook.close();
|
|
|
+ result = "/upload/" + fileName;
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出按项目的甘特图Excel
|
|
|
+ * 列结构:序号 | 项目 | 开始时间 | 结束时间 | 日期1 | 日期2 | ... | 日期N
|
|
|
+ * 每个项目行在对应日期列上用彩色背景色块表示项目时间范围
|
|
|
+ *
|
|
|
+ * @param fileName 文件名(不含扩展名)
|
|
|
+ * @param projectRows 项目数据列表,每个Map包含:projectName, startDate(yyyy-MM-dd), endDate(yyyy-MM-dd)
|
|
|
+ * @param rangeStart 日期范围开始(yyyy-MM-dd)
|
|
|
+ * @param rangeEnd 日期范围结束(yyyy-MM-dd)
|
|
|
+ * @param path 文件保存路径
|
|
|
+ * @return 文件访问路径
|
|
|
+ */
|
|
|
+ public static String exportGanttChartByProject(String fileName,
|
|
|
+ List<Map<String, String>> projectRows,
|
|
|
+ String rangeStart,
|
|
|
+ String rangeEnd,
|
|
|
+ String path) {
|
|
|
+ String result = null;
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFWorkbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook();
|
|
|
+ try {
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFSheet sheet = workbook.createSheet("甘特图");
|
|
|
+
|
|
|
+ java.time.LocalDate startLocalDate = java.time.LocalDate.parse(rangeStart);
|
|
|
+ java.time.LocalDate endLocalDate = java.time.LocalDate.parse(rangeEnd);
|
|
|
+
|
|
|
+ // 计算日期列表
|
|
|
+ List<java.time.LocalDate> dateList = new ArrayList<>();
|
|
|
+ java.time.LocalDate cur = startLocalDate;
|
|
|
+ while (!cur.isAfter(endLocalDate)) {
|
|
|
+ dateList.add(cur);
|
|
|
+ cur = cur.plusDays(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ int totalDays = dateList.size();
|
|
|
+ // 固定列:序号(0), 项目(1), 开始时间(2), 结束时间(3)
|
|
|
+ // 日期列从第4列开始,最后一列为计划工时
|
|
|
+ int dateColStart = 4;
|
|
|
+
|
|
|
+ // ---- 创建样式 ----
|
|
|
+ // 表头样式
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle headerStyle = workbook.createCellStyle();
|
|
|
+ headerStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)68, (byte)114, (byte)196}, null));
|
|
|
+ headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ headerStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ headerStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFFont headerFont = workbook.createFont();
|
|
|
+ headerFont.setBold(true);
|
|
|
+ headerFont.setColor(new XSSFColor(new byte[]{(byte)255, (byte)255, (byte)255}, null));
|
|
|
+ headerStyle.setFont(headerFont);
|
|
|
+
|
|
|
+ // 普通单元格样式
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle normalStyle = workbook.createCellStyle();
|
|
|
+ normalStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ normalStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ normalStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 甘特图色块样式(蓝绿色)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle ganttStyle = workbook.createCellStyle();
|
|
|
+ ganttStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)70, (byte)130, (byte)180}, null));
|
|
|
+ ganttStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ ganttStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ ganttStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ ganttStyle.setBorderLeft(BorderStyle.NONE);
|
|
|
+ ganttStyle.setBorderRight(BorderStyle.NONE);
|
|
|
+
|
|
|
+ // 空白日期单元格样式(浅灰色背景)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle emptyDateStyle = workbook.createCellStyle();
|
|
|
+ emptyDateStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)242, (byte)242, (byte)242}, null));
|
|
|
+ emptyDateStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ emptyDateStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // ---- 创建表头行 ----
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFRow headerRow = sheet.createRow(0);
|
|
|
+ headerRow.setHeightInPoints(25);
|
|
|
+
|
|
|
+ String[] fixedHeaders = {"序号", "项目", "开始时间", "结束时间"};
|
|
|
+ for (int i = 0; i < fixedHeaders.length; i++) {
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell cell = headerRow.createCell(i);
|
|
|
+ cell.setCellValue(fixedHeaders[i]);
|
|
|
+ cell.setCellStyle(headerStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 日期列表头
|
|
|
+ java.time.format.DateTimeFormatter dayFmt = java.time.format.DateTimeFormatter.ofPattern("MM/dd");
|
|
|
+ for (int i = 0; i < totalDays; i++) {
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell cell = headerRow.createCell(dateColStart + i);
|
|
|
+ cell.setCellValue(dateList.get(i).format(dayFmt));
|
|
|
+ cell.setCellStyle(headerStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计划工时列表头(最后一列)
|
|
|
+ int planHoursColIdx = dateColStart + totalDays;
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell planHoursHeaderCell = headerRow.createCell(planHoursColIdx);
|
|
|
+ planHoursHeaderCell.setCellValue("计划工时(h)");
|
|
|
+ planHoursHeaderCell.setCellStyle(headerStyle);
|
|
|
+
|
|
|
+ // ---- 设置列宽 ----
|
|
|
+ sheet.setColumnWidth(0, 8 * 256); // 序号
|
|
|
+ sheet.setColumnWidth(1, 30 * 256); // 项目
|
|
|
+ sheet.setColumnWidth(2, 14 * 256); // 开始时间
|
|
|
+ sheet.setColumnWidth(3, 14 * 256); // 结束时间
|
|
|
+ for (int i = 0; i < totalDays; i++) {
|
|
|
+ sheet.setColumnWidth(dateColStart + i, 5 * 256); // 日期列较窄
|
|
|
+ }
|
|
|
+ sheet.setColumnWidth(planHoursColIdx, 14 * 256); // 计划工时列
|
|
|
+
|
|
|
+ // ---- 创建数据行 ----
|
|
|
+ java.time.format.DateTimeFormatter dateFmt = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ for (int rowIdx = 0; rowIdx < projectRows.size(); rowIdx++) {
|
|
|
+ Map<String, String> projectData = projectRows.get(rowIdx);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFRow dataRow = sheet.createRow(rowIdx + 1);
|
|
|
+ dataRow.setHeightInPoints(22);
|
|
|
+
|
|
|
+ String projectName = projectData.getOrDefault("projectName", "");
|
|
|
+ String projStartStr = projectData.getOrDefault("startDate", "");
|
|
|
+ String projEndStr = projectData.getOrDefault("endDate", "");
|
|
|
+
|
|
|
+ // 序号
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell seqCell = dataRow.createCell(0);
|
|
|
+ seqCell.setCellValue(rowIdx + 1);
|
|
|
+ seqCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 项目名称
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell nameCell = dataRow.createCell(1);
|
|
|
+ nameCell.setCellValue(projectName);
|
|
|
+ nameCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 开始时间
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell startCell = dataRow.createCell(2);
|
|
|
+ startCell.setCellValue(projStartStr);
|
|
|
+ startCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 结束时间
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell endCell = dataRow.createCell(3);
|
|
|
+ endCell.setCellValue(projEndStr);
|
|
|
+ endCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 解析项目的开始和结束日期
|
|
|
+ java.time.LocalDate projStart = null;
|
|
|
+ java.time.LocalDate projEnd = null;
|
|
|
+ try {
|
|
|
+ if (!projStartStr.isEmpty()) projStart = java.time.LocalDate.parse(projStartStr, dateFmt);
|
|
|
+ if (!projEndStr.isEmpty()) projEnd = java.time.LocalDate.parse(projEndStr, dateFmt);
|
|
|
+ } catch (Exception ignored) {}
|
|
|
+
|
|
|
+ // 为每个日期列设置样式
|
|
|
+ for (int dayIdx = 0; dayIdx < totalDays; dayIdx++) {
|
|
|
+ java.time.LocalDate colDate = dateList.get(dayIdx);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell dayCell = dataRow.createCell(dateColStart + dayIdx);
|
|
|
+ dayCell.setCellValue("");
|
|
|
+
|
|
|
+ boolean inRange = projStart != null && projEnd != null
|
|
|
+ && !colDate.isBefore(projStart)
|
|
|
+ && !colDate.isAfter(projEnd);
|
|
|
+
|
|
|
+ if (inRange) {
|
|
|
+ dayCell.setCellStyle(ganttStyle);
|
|
|
+ } else {
|
|
|
+ dayCell.setCellStyle(emptyDateStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计划工时列
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell planHoursCell = dataRow.createCell(planHoursColIdx);
|
|
|
+ String planHoursVal = projectData.getOrDefault("planHours", "");
|
|
|
+ planHoursCell.setCellValue(planHoursVal);
|
|
|
+ planHoursCell.setCellStyle(normalStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ---- 冻结前4列 ----
|
|
|
+ sheet.createFreezePane(dateColStart, 1);
|
|
|
+
|
|
|
+ // ---- 保存文件 ----
|
|
|
+ fileName = fileName + ".xlsx";
|
|
|
+ File dir = new File(path);
|
|
|
+ if (!dir.exists()) {
|
|
|
+ dir.mkdirs();
|
|
|
+ }
|
|
|
+ FileOutputStream os = new FileOutputStream(path + fileName);
|
|
|
+ workbook.write(os);
|
|
|
+ os.flush();
|
|
|
+ os.close();
|
|
|
+ workbook.close();
|
|
|
+ result = "/upload/" + fileName;
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按人员导出甘特图(Excel)
|
|
|
+ * 列结构:序号 | 人员姓名 | 开始时间 | 结束时间 | [日期列...] | 计划工时(h)
|
|
|
+ * 每行代表一条任务记录,在对应日期列用色块显示任务时间范围。
|
|
|
+ *
|
|
|
+ * @param fileName 文件名(不含扩展名)
|
|
|
+ * @param personRows 每行数据,包含 personName/startDate/endDate/planHours 字段
|
|
|
+ * @param rangeStart 日期范围开始(yyyy-MM-dd)
|
|
|
+ * @param rangeEnd 日期范围结束(yyyy-MM-dd)
|
|
|
+ * @param path 文件保存路径
|
|
|
+ * @return 文件访问路径
|
|
|
+ */
|
|
|
+ public static String exportGanttChartByPerson(String fileName,
|
|
|
+ List<Map<String, String>> personRows,
|
|
|
+ String rangeStart,
|
|
|
+ String rangeEnd,
|
|
|
+ String path) {
|
|
|
+ String result = null;
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFWorkbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook();
|
|
|
+ try {
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFSheet sheet = workbook.createSheet("甘特图");
|
|
|
+
|
|
|
+ java.time.LocalDate startLocalDate = java.time.LocalDate.parse(rangeStart);
|
|
|
+ java.time.LocalDate endLocalDate = java.time.LocalDate.parse(rangeEnd);
|
|
|
+
|
|
|
+ // 计算日期列表
|
|
|
+ List<java.time.LocalDate> dateList = new ArrayList<>();
|
|
|
+ java.time.LocalDate cur = startLocalDate;
|
|
|
+ while (!cur.isAfter(endLocalDate)) {
|
|
|
+ dateList.add(cur);
|
|
|
+ cur = cur.plusDays(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ int totalDays = dateList.size();
|
|
|
+ // 固定列:序号(0), 人员姓名(1), 开始时间(2), 结束时间(3)
|
|
|
+ // 日期列从第4列开始,最后一列为计划工时
|
|
|
+ int dateColStart = 4;
|
|
|
+
|
|
|
+ // ---- 创建样式 ----
|
|
|
+ // 表头样式
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle headerStyle = workbook.createCellStyle();
|
|
|
+ headerStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)68, (byte)114, (byte)196}, null));
|
|
|
+ headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ headerStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ headerStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ headerStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFFont headerFont = workbook.createFont();
|
|
|
+ headerFont.setBold(true);
|
|
|
+ headerFont.setColor(new XSSFColor(new byte[]{(byte)255, (byte)255, (byte)255}, null));
|
|
|
+ headerStyle.setFont(headerFont);
|
|
|
+
|
|
|
+ // 普通单元格样式
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle normalStyle = workbook.createCellStyle();
|
|
|
+ normalStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ normalStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ normalStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ normalStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 甘特图色块样式(橙色,区别于项目视图的蓝色)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle ganttStyle = workbook.createCellStyle();
|
|
|
+ ganttStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)255, (byte)153, (byte)51}, null));
|
|
|
+ ganttStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ ganttStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ ganttStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ ganttStyle.setBorderLeft(BorderStyle.NONE);
|
|
|
+ ganttStyle.setBorderRight(BorderStyle.NONE);
|
|
|
+
|
|
|
+ // 空白日期单元格样式(浅灰色背景)
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCellStyle emptyDateStyle = workbook.createCellStyle();
|
|
|
+ emptyDateStyle.setFillForegroundColor(new XSSFColor(new byte[]{(byte)242, (byte)242, (byte)242}, null));
|
|
|
+ emptyDateStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ emptyDateStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ emptyDateStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // ---- 创建表头行 ----
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFRow headerRow = sheet.createRow(0);
|
|
|
+ headerRow.setHeightInPoints(25);
|
|
|
+
|
|
|
+ String[] fixedHeaders = {"序号", "人员姓名", "开始时间", "结束时间"};
|
|
|
+ for (int i = 0; i < fixedHeaders.length; i++) {
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell cell = headerRow.createCell(i);
|
|
|
+ cell.setCellValue(fixedHeaders[i]);
|
|
|
+ cell.setCellStyle(headerStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 日期列表头
|
|
|
+ java.time.format.DateTimeFormatter dayFmt = java.time.format.DateTimeFormatter.ofPattern("MM/dd");
|
|
|
+ for (int i = 0; i < totalDays; i++) {
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell cell = headerRow.createCell(dateColStart + i);
|
|
|
+ cell.setCellValue(dateList.get(i).format(dayFmt));
|
|
|
+ cell.setCellStyle(headerStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计划工时列表头(最后一列)
|
|
|
+ int planHoursColIdx = dateColStart + totalDays;
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell planHoursHeaderCell = headerRow.createCell(planHoursColIdx);
|
|
|
+ planHoursHeaderCell.setCellValue("计划工时(h)");
|
|
|
+ planHoursHeaderCell.setCellStyle(headerStyle);
|
|
|
+
|
|
|
+ // ---- 设置列宽 ----
|
|
|
+ sheet.setColumnWidth(0, 8 * 256); // 序号
|
|
|
+ sheet.setColumnWidth(1, 25 * 256); // 人员姓名
|
|
|
+ sheet.setColumnWidth(2, 14 * 256); // 开始时间
|
|
|
+ sheet.setColumnWidth(3, 14 * 256); // 结束时间
|
|
|
+ for (int i = 0; i < totalDays; i++) {
|
|
|
+ sheet.setColumnWidth(dateColStart + i, 5 * 256); // 日期列较窄
|
|
|
+ }
|
|
|
+ sheet.setColumnWidth(planHoursColIdx, 14 * 256); // 计划工时列
|
|
|
+
|
|
|
+ // ---- 创建数据行 ----
|
|
|
+ java.time.format.DateTimeFormatter dateFmt = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ for (int rowIdx = 0; rowIdx < personRows.size(); rowIdx++) {
|
|
|
+ Map<String, String> personData = personRows.get(rowIdx);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFRow dataRow = sheet.createRow(rowIdx + 1);
|
|
|
+ dataRow.setHeightInPoints(22);
|
|
|
+
|
|
|
+ String personName = personData.getOrDefault("personName", "");
|
|
|
+ String taskStartStr = personData.getOrDefault("startDate", "");
|
|
|
+ String taskEndStr = personData.getOrDefault("endDate", "");
|
|
|
+
|
|
|
+ // 序号
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell seqCell = dataRow.createCell(0);
|
|
|
+ seqCell.setCellValue(rowIdx + 1);
|
|
|
+ seqCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 人员姓名
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell nameCell = dataRow.createCell(1);
|
|
|
+ nameCell.setCellValue(personName);
|
|
|
+ nameCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 开始时间
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell startCell = dataRow.createCell(2);
|
|
|
+ startCell.setCellValue(taskStartStr);
|
|
|
+ startCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 结束时间
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell endCell = dataRow.createCell(3);
|
|
|
+ endCell.setCellValue(taskEndStr);
|
|
|
+ endCell.setCellStyle(normalStyle);
|
|
|
+
|
|
|
+ // 解析任务的开始和结束日期
|
|
|
+ java.time.LocalDate taskStart = null;
|
|
|
+ java.time.LocalDate taskEnd = null;
|
|
|
+ try {
|
|
|
+ if (!taskStartStr.isEmpty()) taskStart = java.time.LocalDate.parse(taskStartStr, dateFmt);
|
|
|
+ if (!taskEndStr.isEmpty()) taskEnd = java.time.LocalDate.parse(taskEndStr, dateFmt);
|
|
|
+ } catch (Exception ignored) {}
|
|
|
+
|
|
|
+ // 为每个日期列设置样式
|
|
|
+ for (int dayIdx = 0; dayIdx < totalDays; dayIdx++) {
|
|
|
+ java.time.LocalDate colDate = dateList.get(dayIdx);
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell dayCell = dataRow.createCell(dateColStart + dayIdx);
|
|
|
+ dayCell.setCellValue("");
|
|
|
+
|
|
|
+ boolean inRange = taskStart != null && taskEnd != null
|
|
|
+ && !colDate.isBefore(taskStart)
|
|
|
+ && !colDate.isAfter(taskEnd);
|
|
|
+
|
|
|
+ if (inRange) {
|
|
|
+ dayCell.setCellStyle(ganttStyle);
|
|
|
+ } else {
|
|
|
+ dayCell.setCellStyle(emptyDateStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计划工时列
|
|
|
+ org.apache.poi.xssf.usermodel.XSSFCell planHoursCell = dataRow.createCell(planHoursColIdx);
|
|
|
+ String planHoursVal = personData.getOrDefault("planHours", "");
|
|
|
+ planHoursCell.setCellValue(planHoursVal);
|
|
|
+ planHoursCell.setCellStyle(normalStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ---- 冻结前4列 ----
|
|
|
+ sheet.createFreezePane(dateColStart, 1);
|
|
|
+
|
|
|
+ // ---- 保存文件 ----
|
|
|
+ fileName = fileName + ".xlsx";
|
|
|
+ File dir = new File(path);
|
|
|
+ if (!dir.exists()) {
|
|
|
+ dir.mkdirs();
|
|
|
+ }
|
|
|
+ FileOutputStream os = new FileOutputStream(path + fileName);
|
|
|
+ workbook.write(os);
|
|
|
+ os.flush();
|
|
|
+ os.close();
|
|
|
+ workbook.close();
|
|
|
+ result = "/upload/" + fileName;
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
}
|