|
|
|
|
@ -3,6 +3,7 @@ package org.dromara.oa.erp.controller;
|
|
|
|
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
|
|
|
|
import jakarta.servlet.http.HttpServletResponse;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
import org.dromara.common.core.domain.R;
|
|
|
|
|
import org.dromara.common.log.annotation.Log;
|
|
|
|
|
import org.dromara.common.log.enums.BusinessType;
|
|
|
|
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
|
|
|
|
@ -22,6 +23,10 @@ import cn.idev.excel.metadata.data.WriteCellData;
|
|
|
|
|
import cn.idev.excel.metadata.Head;
|
|
|
|
|
import org.apache.poi.ss.usermodel.Cell;
|
|
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
|
|
|
|
import org.apache.poi.ss.usermodel.Workbook;
|
|
|
|
|
import org.apache.poi.ss.usermodel.CellStyle;
|
|
|
|
|
import org.apache.poi.ss.usermodel.IndexedColors;
|
|
|
|
|
import org.apache.poi.ss.usermodel.FillPatternType;
|
|
|
|
|
import java.net.URLEncoder;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
@ -31,6 +36,9 @@ import java.math.BigDecimal;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collections;
|
|
|
|
|
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.ProjectPersonnelReportVo;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 工时报表
|
|
|
|
|
*
|
|
|
|
|
@ -55,10 +63,23 @@ public class ErpTimesheetReportController {
|
|
|
|
|
return reportService.queryProjectManHourList(bo, startTime, endTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取项目工时人员统计报表
|
|
|
|
|
*/
|
|
|
|
|
@SaCheckPermission("oa:erp:timesheetReport:projectPersonnel:list")
|
|
|
|
|
@GetMapping("/projectPersonnelReport")
|
|
|
|
|
public R<ProjectPersonnelReportVo> getProjectPersonnelReport(@RequestParam(required = true) Long deptId,
|
|
|
|
|
String startTime, String endTime) {
|
|
|
|
|
if (deptId == null) {
|
|
|
|
|
return R.fail("部门ID不能为空");
|
|
|
|
|
}
|
|
|
|
|
return R.ok(reportService.queryProjectPersonnelReport(deptId, startTime, endTime));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 导出项目工时统计列表
|
|
|
|
|
*/
|
|
|
|
|
@SaCheckPermission("oa:erp:timesheetReport:projectManHour:export")
|
|
|
|
|
@SaCheckPermission("oa/erp:timesheetInfo:export")
|
|
|
|
|
@Log(title = "项目工时统计报表", businessType = BusinessType.EXPORT)
|
|
|
|
|
@PostMapping("/exportProjectManHour")
|
|
|
|
|
public void exportProjectManHour(ProjectManHourReportVo bo, String startTime, String endTime,
|
|
|
|
|
@ -113,6 +134,7 @@ public class ErpTimesheetReportController {
|
|
|
|
|
.head(heads)
|
|
|
|
|
.sheet("项目工时统计报表")
|
|
|
|
|
.registerWriteHandler(new ProjectCellMergeStrategy(list))
|
|
|
|
|
.registerWriteHandler(new ProjectRowHighlightStrategy(list))
|
|
|
|
|
.doWrite(list);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
@ -120,6 +142,106 @@ public class ErpTimesheetReportController {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 导出项目人员工时统计报表
|
|
|
|
|
*/
|
|
|
|
|
@SaCheckPermission("oa/erp:timesheetInfo:export")
|
|
|
|
|
@Log(title = "项目人员工时统计报表", businessType = BusinessType.EXPORT)
|
|
|
|
|
@PostMapping("/exportProjectPersonnel")
|
|
|
|
|
public void exportProjectPersonnel(Long deptId, String startTime, String endTime, HttpServletResponse response) {
|
|
|
|
|
try {
|
|
|
|
|
ProjectPersonnelReportVo result = reportService.queryProjectPersonnelReport(deptId, startTime, endTime);
|
|
|
|
|
List<Map<String, Object>> rows = result.getRows();
|
|
|
|
|
List<ProjectPersonnelReportVo.ReportColumn> columns = result.getColumns();
|
|
|
|
|
|
|
|
|
|
// 1. 构建表头
|
|
|
|
|
List<List<String>> heads = new ArrayList<>();
|
|
|
|
|
heads.add(Collections.singletonList("部门"));
|
|
|
|
|
heads.add(Collections.singletonList("项目名称"));
|
|
|
|
|
heads.add(Collections.singletonList("项目编号"));
|
|
|
|
|
heads.add(Collections.singletonList("汇总"));
|
|
|
|
|
|
|
|
|
|
// 动态人员列
|
|
|
|
|
List<String> userKeys = new ArrayList<>();
|
|
|
|
|
if (columns != null) {
|
|
|
|
|
for (ProjectPersonnelReportVo.ReportColumn col : columns) {
|
|
|
|
|
heads.add(Collections.singletonList(col.getLabel()));
|
|
|
|
|
userKeys.add(col.getProp());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
heads.add(Collections.singletonList("备注"));
|
|
|
|
|
|
|
|
|
|
// 2. 构建数据行
|
|
|
|
|
List<List<Object>> dataList = new ArrayList<>();
|
|
|
|
|
if (rows != null) {
|
|
|
|
|
for (Map<String, Object> row : rows) {
|
|
|
|
|
List<Object> dataRow = new ArrayList<>();
|
|
|
|
|
dataRow.add(row.get("deptName"));
|
|
|
|
|
dataRow.add(row.get("projectName"));
|
|
|
|
|
dataRow.add(row.get("projectCode"));
|
|
|
|
|
dataRow.add(row.get("rowTotal")); // 对应“汇总”列
|
|
|
|
|
|
|
|
|
|
// 人员列数据
|
|
|
|
|
for (String key : userKeys) {
|
|
|
|
|
Object hours = row.get(key);
|
|
|
|
|
dataRow.add(hours != null ? hours : "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dataRow.add(row.get("remark"));
|
|
|
|
|
dataList.add(dataRow);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
|
|
response.setCharacterEncoding("utf-8");
|
|
|
|
|
String fileName = URLEncoder.encode("项目人员工时统计报表", "UTF-8").replaceAll("\\+", "%20");
|
|
|
|
|
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
|
|
|
|
|
|
|
|
|
// 3. 导出 (注册自定义样式策略)
|
|
|
|
|
FastExcel.write(response.getOutputStream())
|
|
|
|
|
.head(heads)
|
|
|
|
|
.sheet("项目人员工时统计")
|
|
|
|
|
.registerWriteHandler(new MapRowHighlightStrategy(rows))
|
|
|
|
|
.doWrite(dataList);
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
throw new RuntimeException("导出失败");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Map数据的行高亮策略
|
|
|
|
|
*/
|
|
|
|
|
public static class MapRowHighlightStrategy implements CellWriteHandler {
|
|
|
|
|
private final List<Map<String, Object>> dataList;
|
|
|
|
|
|
|
|
|
|
public MapRowHighlightStrategy(List<Map<String, Object>> dataList) {
|
|
|
|
|
this.dataList = dataList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
|
|
|
|
|
List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
|
|
|
|
|
if (isHead || relativeRowIndex == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (dataList != null && relativeRowIndex < dataList.size()) {
|
|
|
|
|
Map<String, Object> rowData = dataList.get(relativeRowIndex);
|
|
|
|
|
Boolean isCrossDept = (Boolean) rowData.get("isCrossDept");
|
|
|
|
|
if (Boolean.TRUE.equals(isCrossDept)) {
|
|
|
|
|
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
|
|
|
|
CellStyle cellStyle = workbook.createCellStyle();
|
|
|
|
|
cellStyle.cloneStyleFrom(cell.getCellStyle());
|
|
|
|
|
cellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
|
|
|
|
|
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
|
|
cell.setCellStyle(cellStyle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 自定义单元格合并策略(按项目ID合并跨部门工时列)
|
|
|
|
|
*/
|
|
|
|
|
@ -176,4 +298,38 @@ public class ErpTimesheetReportController {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 自定义行高亮策略
|
|
|
|
|
*/
|
|
|
|
|
public static class ProjectRowHighlightStrategy implements CellWriteHandler {
|
|
|
|
|
private final List<ProjectManHourReportVo> list;
|
|
|
|
|
// 黄色背景样式
|
|
|
|
|
private final static short YELLOW_COLOR_INDEX = org.apache.poi.ss.usermodel.IndexedColors.LIGHT_YELLOW
|
|
|
|
|
.getIndex();
|
|
|
|
|
|
|
|
|
|
public ProjectRowHighlightStrategy(List<ProjectManHourReportVo> list) {
|
|
|
|
|
this.list = list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
|
|
|
|
|
List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
|
|
|
|
|
if (isHead || relativeRowIndex == null || relativeRowIndex >= list.size()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProjectManHourReportVo vo = list.get(relativeRowIndex);
|
|
|
|
|
// 如果 crossDeptHours 不为空(说明有跨部门工时),则整行标黄
|
|
|
|
|
// 注意:前面的逻辑保证了只有不相等时 crossDeptHours 才不为空
|
|
|
|
|
if (vo.getCrossDeptHours() != null) {
|
|
|
|
|
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
|
|
|
|
org.apache.poi.ss.usermodel.CellStyle cellStyle = workbook.createCellStyle();
|
|
|
|
|
cellStyle.cloneStyleFrom(cell.getCellStyle());
|
|
|
|
|
cellStyle.setFillForegroundColor(YELLOW_COLOR_INDEX);
|
|
|
|
|
cellStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
|
|
|
|
|
cell.setCellStyle(cellStyle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|