人力成本管理

master
OathK1per 2021-11-10 17:40:58 +08:00
parent e63c385be1
commit e2e24f6f77
27 changed files with 1887 additions and 0 deletions

View File

@ -71,6 +71,10 @@ dependencies {
compile group: 'org.apache.xmlgraphics', name: 'batik-codec', version: '1.7'
/*activiti end*/
// https://mvnrepository.com/artifact/cn.afterturn/easypoi-base
compile group: 'cn.afterturn', name: 'easypoi-base', version: '4.2.0'
// https://mvnrepository.com/artifact/cn.afterturn/easypoi-annotation
compile group: 'cn.afterturn', name: 'easypoi-annotation', version: '4.2.0'
compileOnly 'org.springframework.boot:spring-boot-configuration-processor'

View File

@ -0,0 +1,169 @@
package cn.palmte.work.controller.backend;
import cn.palmte.work.bean.ResponseMsg;
import cn.palmte.work.model.*;
import cn.palmte.work.service.HumanCostService;
import cn.palmte.work.utils.Utils;
import cn.palmte.work.utils.excel.ExcelUtil;
import cn.palmte.work.utils.excel.ExportUtils;
import com.alibaba.fastjson.JSON;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.poifs.filesystem.OfficeXmlFileException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import top.jfunc.common.db.bean.Page;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @author Yuanping Zhang
* @date 2021/11/1
*/
@Controller
@RequestMapping("/humanCost")
public class HumanCostController extends BaseController{
private static final Logger logger = LoggerFactory.getLogger(HumanCostController.class);
@Autowired
private DeptRepository deptRepository;
@Autowired
private HumanCostService humanCostService;
@Autowired
private AdminRepository adminRepository;
@Autowired
private ProjectRepository projectRepository;
/**
*
*/
@RequestMapping("/list")
public String list(@RequestParam(value = KEYWORDS,required = false) String keywords,
@RequestParam(value = PAGE_NUMBER, defaultValue = DEFAULT_PAGE_NUMBER) int pageNumber,
@RequestParam(value = PAGE_SIZE, defaultValue = DEFAULT_PAGE_SIZE) int pageSize,
Map<String, Object> model) {
//当前登录人的角色类型
Admin admin = getAdmin();
int roleId = admin.getRoleId();
List<Project> selfProjects = projectRepository.findByCreator(admin.getId(), new Date());
if (roleId <= 18) {
model.put("deptVary", 1);
model.put("deptList", deptRepository.findAll());
model.put("projectList", projectRepository.findAll());
model.put("showSalary", 1);
} else if (roleId <= 20) {
model.put("deptVary", -1);
model.put("deptList", new ArrayList<>());
model.put("projectList", projectRepository.findByDeptId(admin.getDeptId()));
model.put("showSalary", -1);
} else if (selfProjects != null) {
model.put("deptVary", -1);
model.put("deptList", new ArrayList<>());
model.put("projectList", selfProjects);
model.put("showSalary", -1);
} else {
keywords = setDate(keywords);
model.put("keywords",keywords);
ConcurrentHashMap<String, String> searchInfo = getSearchInfo(keywords,model);
if (searchInfo.containsKey("time")) {
String time = searchInfo.get("time");
if (time.length() == 7) {
String individualTime = time.substring(0, 4) + "年" + time.substring(5, 7) + "月";
model.put("individualTime", individualTime);
}
}
model.put("pager",humanCostService.individual(searchInfo, admin.getId(), pageNumber, pageSize));
return "admin/human_cost_individual";
}
keywords = setDate(keywords);
model.put("keywords",keywords);
ConcurrentHashMap<String, String> searchInfo = getSearchInfo(keywords,model);
Page<ProjectUserTime> project = humanCostService.project(searchInfo, admin, pageNumber, pageSize);
List<ProjectUserTime> staff = humanCostService.staff(searchInfo, admin);
Map<String, BigDecimal> map = humanCostService.map(searchInfo, admin, pageNumber, pageSize);
model.put("pager", project);
model.put("staff", staff);
model.put("map", map);
return "admin/human_cost_list";
}
private String setDate(String keywords) {
if (keywords == null || keywords.length() == 0) {
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM");
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1);
date = calendar.getTime();
String accDate = sf.format(date);
keywords = "{time: '" + accDate + "'}";
}
return keywords;
}
/**
*
*/
@RequestMapping("/export")
public void export(@RequestParam(value = KEYWORDS,required = false) String keywords,
HttpServletResponse response) throws Exception{
Admin admin = getAdmin();
Map<String, String> searchInfo = getSearchInfo(keywords);
downloadHeader(response , Utils.generateExcelName("人力成本表"));
Page<ProjectUserTime> project = humanCostService.project((ConcurrentHashMap<String, String>) searchInfo, admin, 1, 5000);
List<ProjectUserTime> staff = humanCostService.staff((ConcurrentHashMap<String, String>) searchInfo, admin);
Map<String, BigDecimal> map = humanCostService.map((ConcurrentHashMap<String, String>) searchInfo, admin, 1, 5000);
String[] headers = humanCostService.getHeaders(staff);
String[] columns = humanCostService.getColumns(project.getList());
ExportUtils.exportHumanCost(headers, columns, staff, project.getList(),
response.getOutputStream() , map);
}
/**
*
*/
@RequestMapping("/template")
public void importTemplate(HttpServletResponse response) throws IOException {
String[] headers = new String[]{"项目名称", "人员1", "人员2"};
String[] columns = new String[]{""};
downloadHeader(response , Utils.generateExcelName("人力成本导入模板"));
ExportUtils exportUtils = new ExportUtils(headers);
List<String> data = new ArrayList<>();
data.add("成本");
data.add("项目1");
data.add("项目2");
exportUtils.exportTemplate(columns , data, "yyyy-MM-dd HH:mm:ss" , 1);
exportUtils.write(response.getOutputStream());
}
@RequestMapping("/batchImport")
@ResponseBody
public String batchImport(MultipartFile file) {
try {
String date = "2021-11";
Map<String, String> title = new HashMap<>();
Collection<Map> maps = ExcelUtil.importHumanCost(Map.class, file.getInputStream(),
title,0);
return JSON.toJSONString(humanCostService.check(maps, title, date));
} catch (OfficeXmlFileException | EncryptedDocumentException | InvalidFormatException e) {
logger.error("", e);
return JSON.toJSONString(ResponseMsg.buildFailedMsg("格式错误请上传excel 2003/2007格式文件"));
} catch (Exception e) {
return JSON.toJSONString(ResponseMsg.buildFailedMsg("导入数据失败,请联系管理员"));
}
}
}

View File

@ -1,6 +1,19 @@
package cn.palmte.work.model;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.Date;
import java.util.List;
public interface ProjectRepository extends JpaRepository<Project,Integer> {
@Query(value = "select * from project where name = ?1", nativeQuery = true)
Project findByProjectName(String projectName);
@Query(value = "select * from project where dept_id = ?1", nativeQuery = true)
List<Project> findByDeptId(int deptId);
@Query(value = "select * from project where creator_id = ?1 and end_date >= ?2 and start_date <= ?2", nativeQuery = true)
List<Project> findByCreator(int id, Date date);
}

View File

@ -0,0 +1,149 @@
package cn.palmte.work.model;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author Yuanping Zhang
* @date 2021/11/1
*/
@Entity
@Table(name = "project_user_time")
public class ProjectUserTime {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@GenericGenerator(name = "persistenceGenerator", strategy = "increment")
private int id;
@Column(name = "project_id")
private int projectId;
@Column(name = "project_name")
private String projectName;
@Column(name = "user_id")
private int userId;
@Column(name = "user_salary")
private BigDecimal userSalary;
@Column(name = "user_cost")
private BigDecimal userCost;
@Column(name = "user_name")
private String userName;
@Column(name = "time")
private Date time;
@Column(name = "created_by")
private int createdBy;
@Column(name = "created_time")
private Date createdTime;
@Transient
private BigDecimal costSum;
@Transient
private BigDecimal salarySum;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getProjectId() {
return projectId;
}
public void setProjectId(int projectId) {
this.projectId = projectId;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public BigDecimal getUserSalary() {
return userSalary;
}
public void setUserSalary(BigDecimal userSalary) {
this.userSalary = userSalary;
}
public BigDecimal getUserCost() {
return userCost;
}
public void setUserCost(BigDecimal userCost) {
this.userCost = userCost;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public int getCreatedBy() {
return createdBy;
}
public void setCreatedBy(int createdBy) {
this.createdBy = createdBy;
}
public Date getCreatedTime() {
return createdTime;
}
public void setCreatedTime(Date createdTime) {
this.createdTime = createdTime;
}
public BigDecimal getCostSum() {
return costSum;
}
public void setCostSum(BigDecimal costSum) {
this.costSum = costSum;
}
public BigDecimal getSalarySum() {
return salarySum;
}
public void setSalarySum(BigDecimal salarySum) {
this.salarySum = salarySum;
}
}

View File

@ -0,0 +1,16 @@
package cn.palmte.work.model;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.Date;
/**
* @author Yuanping Zhang
* @date 2021/11/1
*/
public interface ProjectUserTimeRepository extends JpaRepository<ProjectUserTime, Integer> {
@Query(value = "select * from project_user_time where user_id = ? and project_id = ? and time = ? limit 1", nativeQuery = true)
public ProjectUserTime findByUserIdAndProjectIdAndTime(int userId, int projectId, Date time);
}

View File

@ -0,0 +1,21 @@
package cn.palmte.work.model;
import top.jfunc.common.db.bean.Page;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Yuanping Zhang
* @date 2021/11/1
*/
public interface ProjectUserTimeRepositoryCustom {
Page<ProjectUserTime> project(ConcurrentHashMap<String, String> searchInfo, Admin admin, int pageNumber, int pageSize);
List<ProjectUserTime> staff(ConcurrentHashMap<String, String> searchInfo, Admin admin);
List<ProjectUserTime> list(ConcurrentHashMap<String, String> searchInfo, Admin admin, int pageNumber, int pageSize);
Page<ProjectUserTime> individual(ConcurrentHashMap<String, String> searchInfo, Integer id, int pageNumber, int pageSize);
}

View File

@ -0,0 +1,73 @@
package cn.palmte.work.model;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.utils.Pagination;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Yuanping Zhang
* @date 2021/11/1
*/
@Repository
public class ProjectUserTimeRepositoryImpl implements ProjectUserTimeRepositoryCustom {
@Autowired
private Pagination pagination;
@Override
public Page<ProjectUserTime> project(ConcurrentHashMap<String, String> searchInfo, Admin admin, int pageNumber, int pageSize) {
QueryHelper helper = new QueryHelper("select put.project_name, put.project_id, put.user_id, put.user_cost, sum(put.user_cost) as costSum, sum(put.user_cost * put.user_salary) as salarySum", "from project_user_time put");
helper.leftJoin("project proj", "put.project_id = proj.id");
helper.leftJoin("dept", "proj.dept_id = dept.id");
helper = searchQuery(helper, searchInfo, admin);
helper.addGroupProperty("put.project_id");
return pagination.paginate(helper.getSql(), ProjectUserTime.class, pageNumber, pageSize);
}
@Override
public List<ProjectUserTime> staff(ConcurrentHashMap<String, String> searchInfo, Admin admin) {
QueryHelper helper = new QueryHelper("select put.user_id, put.user_name, put.user_salary", "from project_user_time put");
helper.leftJoin("project proj", "put.project_id = proj.id");
helper.leftJoin("dept", "proj.dept_id = dept.id");
searchQuery(helper, searchInfo, admin);
helper.addGroupProperty("put.user_id");
return pagination.find(helper.getSql(), ProjectUserTime.class);
}
@Override
public List<ProjectUserTime> list(ConcurrentHashMap<String, String> searchInfo, Admin admin, int pageNumber, int pageSize) {
QueryHelper helper = new QueryHelper("select put.project_name, put.project_id, put.user_id, put.user_cost", "from project_user_time put");
helper.leftJoin("project proj", "put.project_id = proj.id");
helper.leftJoin("dept", "proj.dept_id = dept.id");
searchQuery(helper, searchInfo, admin);
return pagination.find(helper.getSql(), ProjectUserTime.class);
}
@Override
public Page<ProjectUserTime> individual(ConcurrentHashMap<String, String> searchInfo, Integer id, int pageNumber, int pageSize) {
QueryHelper helper = new QueryHelper("select put.*", "from project_user_time put");
helper.addCondition("put.user_id = ?", id);
helper.addCondition(searchInfo.containsKey("time"),"put.time = ?", searchInfo.get("time") + "-01 00:00:00");
helper.addOrderProperty("created_time", false);
return pagination.paginate(helper.getSql(), ProjectUserTime.class, pageNumber, pageSize);
}
private QueryHelper searchQuery(QueryHelper helper, ConcurrentHashMap<String, String> searchInfo, Admin admin) {
helper.addCondition(searchInfo.containsKey("time"),"put.time = ?", searchInfo.get("time") + "-01 00:00:00");
helper.addCondition(searchInfo.containsKey("projectId") && !"-1".equals(searchInfo.get("projectId")), "put.project_id = ?", searchInfo.get("projectId"));
helper.addCondition(searchInfo.containsKey("deptId") && !"-1".equals(searchInfo.get("deptId")), "dept.id = ?", searchInfo.get("deptId"));
helper.addCondition(searchInfo.containsKey("userName"), "put.user_name like ?", "%'" + searchInfo.get("userName") + "'%");
if (admin.getRoleId() >= 19 && admin.getRoleId() <= 20) {
helper.addCondition("proj.dept_id = ?", admin.getDeptId());
} else if (admin.getRoleId() > 20) {
helper.addCondition("proj.creator_id = ?", admin.getId());
}
return helper;
}
}

View File

@ -0,0 +1,208 @@
package cn.palmte.work.service;
import cn.palmte.work.bean.ResponseMsg;
import cn.palmte.work.model.*;
import cn.palmte.work.utils.DateKit;
import cn.palmte.work.utils.InterfaceUtil;
import cn.palmte.work.utils.StrKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.jfunc.common.db.bean.Page;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Yuanping Zhang
* @date 2021/11/1
*/
@Service
public class HumanCostService {
private static final Logger logger = LoggerFactory.getLogger(HumanCostService.class);
@Autowired
private ProjectUserTimeRepositoryImpl projectUserTimeRepositoryImpl;
@Autowired
private ProjectUserTimeRepository projectUserTimeRepository;
@Autowired
private ProjectRepository projectRepository;
@Autowired
private AdminRepository adminRepository;
public Page<ProjectUserTime> project(ConcurrentHashMap<String, String> searchInfo, Admin admin, int pageNumber, int pageSize) {
Page<ProjectUserTime> list = projectUserTimeRepositoryImpl.project(searchInfo, admin, pageNumber, pageSize);
return list;
}
public List<ProjectUserTime> staff(ConcurrentHashMap<String, String> searchInfo, Admin admin) {
List<ProjectUserTime> list = projectUserTimeRepositoryImpl.staff(searchInfo, admin);
return list;
}
public List<ProjectUserTime> list(ConcurrentHashMap<String, String> searchInfo, Admin admin, int pageNumber, int pageSize) {
List<ProjectUserTime> list = projectUserTimeRepositoryImpl.list(searchInfo, admin, pageNumber, pageSize);
return list;
}
public Map<String, BigDecimal> map(ConcurrentHashMap<String, String> searchInfo, Admin admin, int pageNumber, int pageSize) {
List<ProjectUserTime> list = projectUserTimeRepositoryImpl.list(searchInfo, admin, pageNumber, pageSize);
Map<String, BigDecimal> map = new HashMap<>(16);
for (ProjectUserTime put : list) {
map.put(put.getProjectName() + put.getUserId(), put.getUserCost());
}
return map;
}
public Page<ProjectUserTime> individual(ConcurrentHashMap<String, String> searchInfo, Integer id, int pageNumber, int pageSize) {
Page<ProjectUserTime> list = projectUserTimeRepositoryImpl.individual(searchInfo, id, pageNumber, pageSize);
return list;
}
@Transactional(rollbackFor = Exception.class)
public ResponseMsg check(Collection<Map> excelMap, Map<String, String> title, String date) {
int successCount = 0;
int errorCount = 0;
Admin admin;
BigDecimal userCost;
BigDecimal userSalary;
List<String> errorList = new ArrayList<>();
Set<String> projectSet = new HashSet<>();
Map<String, BigDecimal> staffCost = new HashMap<>();
List<ProjectUserTime> saveList = new ArrayList<>();
if(excelMap.size() == 0){
ResponseMsg msg = ResponseMsg.buildFailedMsg("请填写人力成本数据!");
msg.setData(errorList);
return msg;
}
if (title.size() < excelMap.size() - 1) {
ResponseMsg msg = ResponseMsg.buildFailedMsg("人员名单存在重复!");
msg.setData(errorList);
return msg;
}
for (Map m : excelMap) {
try {
Object o1 = m.get("项目名称");
if (o1 == null || StrKit.isBlank(o1.toString())) {
throw new Exception("项目名称不能为空");
}
if ("成本".equals(o1.toString())) {
continue;
}
String projectName = (String)o1;
Project project = projectRepository.findByProjectName(projectName);
if (project == null) {
throw new Exception(projectName + "不存在");
} else if (project.getStatus() != 15 || project.getApproveStatusSettle() == 1 || project.getApproveStatusSettle() == 2) {
throw new Exception(projectName + "不在项目结算的可编辑状态");
}
if (projectSet.contains(projectName)) {
throw new Exception("项目名称存在重复!");
} else {
projectSet.add(projectName);
}
for (String key : title.keySet()) {
if (key == null || StrKit.isBlank(key)) {
throw new Exception("人员名称不能为空");
}
admin = adminRepository.getAdminByRealName(key);
if (admin == null) {
throw new Exception(key + "不存在");
}
Object o2 = m.get(key);
if (o2 == null || StrKit.isBlank(o2.toString())) {
throw new Exception(projectName + " " + key + "的月份额不能为空");
}
if (o2.toString().matches("(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){1,2})?") && BigDecimal.valueOf(1).compareTo(new BigDecimal(o2.toString())) > -1 && BigDecimal.valueOf(0).compareTo(new BigDecimal(o2.toString())) < 1) {
userCost = new BigDecimal(o2.toString());
} else {
throw new Exception(projectName + " " + key + "的月份额需为0到1之间最多两位小数的数字");
}
String o3 = title.get(key);
if (o3 == null || StrKit.isBlank(o3)) {
throw new Exception(projectName + " " + key + "的成本不能为空");
}
if (o3.matches("(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){1,2})?") && BigDecimal.valueOf(0).compareTo(new BigDecimal(o3)) < 1) {
userSalary = new BigDecimal(o3);
} else {
throw new Exception(projectName + " " + key + "的成本需为大于0的最多两位小数的的数字");
}
ProjectUserTime put = projectUserTimeRepository.findByUserIdAndProjectIdAndTime(admin.getId(), project.getId(), DateKit.getDate(date + "-01 00:00:00", DateKit.TIME_FORMAT));
if (put == null) {
put = new ProjectUserTime();
put.setCreatedBy(InterfaceUtil.getAdminId());
put.setCreatedTime(new Date());
put.setTime(DateKit.getDate(date, "yyyy-MM"));
put.setProjectId(project.getId());
put.setProjectName(project.getName());
put.setUserName(admin.getRealName());
put.setUserId(admin.getId());
}
put.setUserCost(userCost);
put.setUserSalary(userSalary);
staffCost.put(key, userCost.add(staffCost.getOrDefault(key, BigDecimal.valueOf(0))));
saveList.add(put);
}
successCount++;
} catch (Exception e) {
logger.error("", e);
errorCount++;
errorList.add(e.getMessage());
}
}
for (String key : staffCost.keySet()) {
try {
if (staffCost.getOrDefault(key, BigDecimal.valueOf(0)).compareTo(BigDecimal.valueOf(1)) != 0) {
throw new Exception(key + "的月份额总计不为1");
}
} catch (Exception e) {
logger.error("", e);
errorCount++;
errorList.add(e.getMessage());
}
}
if (errorCount == 0) {
projectUserTimeRepository.save(saveList);
}
final ResponseMsg msg = ResponseMsg.buildSuccessMsg(String.format("成功:%d, 失败:%d", successCount, errorCount));
msg.setData(errorList);
return msg;
}
public static void main(String[] args) {
System.out.println("0.66".matches("(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){1,2})?"));
System.out.println("0.66".matches("(([1-9]{1}\\d*)|([0]{1}))(\\.(\\d){1,2})?"));
}
public String[] getHeaders(List<ProjectUserTime> staff) {
String[] headers = new String[staff.size() + 3];
headers[0] = "项目名称";
headers[1] = "人力总计";
headers[2] = "成本总计";
int i = 3;
for (ProjectUserTime person : staff) {
headers[i++] = person.getUserName();
}
return headers;
}
public String[] getColumns(List<ProjectUserTime> project) {
String[] columns = new String[project.size()];
int i = 0;
for (ProjectUserTime p : project) {
columns[i++] = p.getProjectName();
}
return columns;
}
}

View File

@ -664,4 +664,89 @@ public class ExcelUtil{
return src.replaceAll(" ", "");
}
/**
* ExcelvoList
*
* @param clazz
* voClass
* @param inputStream
* excel
* @param arrayCount
* vo,index,.
* @return voList
* @throws InvalidFormatException
* @throws EncryptedDocumentException
* @throws RuntimeException
*/
@SuppressWarnings("unchecked")
public static <T> Collection<T> importHumanCost(Class<T> clazz, InputStream inputStream, Map<String, String> title,
Integer... arrayCount) throws EncryptedDocumentException, InvalidFormatException {
Workbook workBook = null;
try{
workBook = (Workbook) WorkbookFactory.create(inputStream);
}
catch(IOException e){
LG.error(e.toString(), e);
}
List<T> list = new ArrayList<T>();
Sheet sheet = workBook.getSheetAt(0);
Iterator<Row> rowIterator = sheet.rowIterator();
// Map<title,index>
Map<String, Integer> titleMap = new HashMap<String, Integer>();
while(rowIterator.hasNext()){
Row row = rowIterator.next();
if(row.getRowNum() == 0){
// 解析map用的key,就是excel标题行
Iterator<Cell> cellIterator = row.cellIterator();
Integer index = 0;
while(cellIterator.hasNext()){
String value = cellIterator.next().getStringCellValue();
titleMap.put(value, index);
index++;
}
continue;
}
// 整行都空,就跳过
boolean allRowIsNull = true;
Iterator<Cell> cellIterator = row.cellIterator();
while(cellIterator.hasNext()){
Object cellValue = getCellValue(cellIterator.next());
if(cellValue != null){
allRowIsNull = false;
break;
}
}
if(allRowIsNull){
LG.warn("Excel row " + row.getRowNum() + " all row value is null!");
continue;
}
T t = null;
StringBuffer log = new StringBuffer();
if(clazz == Map.class){
Map<String, Object> map = new HashMap<String, Object>();
for(String k : titleMap.keySet()){
Integer index = titleMap.get(k);
Cell cell = row.getCell(index);
String value = null;
if(cell != null){
int cellType = row.getCell(index).getCellType();
if(cellType == Cell.CELL_TYPE_NUMERIC){
row.getCell(index).setCellType(Cell.CELL_TYPE_STRING);
}
value = row.getCell(index).getStringCellValue();
}
if (!"项目名称".equals(k)) {
if (row.getRowNum() == 1) {
title.put(k, value);
}
}
map.put(k, value);
}
list.add((T)map);
}
}
return list;
}
}

View File

@ -1,5 +1,6 @@
package cn.palmte.work.utils.excel;
import cn.palmte.work.model.ProjectUserTime;
import cn.palmte.work.utils.DateKit;
import cn.palmte.work.utils.ObjectKit;
import org.apache.poi.hssf.util.HSSFColor;
@ -230,4 +231,101 @@ public class ExportUtils {
}
return exportUtils;
}
/**
*
*
* @param dataset
* JavaBean
* @param pattern
* "yyyy-MM-dd"
* @param rowIndex
*
* @throws IOException
*/
public void exportTemplate(String[] columns, List<?> dataset, String pattern, int rowIndex) throws IOException{
// 遍历集合数据,产生数据行
int index = rowIndex;
Font font3 = workbook.createFont();
font3.setColor(HSSFColor.HSSFColorPredefined.BLUE.getIndex());
for(int i = 0; i < dataset.size(); i++){
Row row = sheet.createRow(index++);
Object data = dataset.get(i);
for(short col = 0; col < columns.length; col++){
Cell cell = row.createCell(col);
cell.setCellStyle(style2);
Object value = data;
cell.setCellType(CellType.STRING);
if(value == null){
cell.setCellType(CellType.STRING);
cell.setCellValue("");
}else{
cell.setCellType(CellType.STRING);
cell.setCellValue(value.toString());
}
}
if(i != 0 && i % ROW_ACCESS_WINDOW_SIZE == 0){
((SXSSFSheet)sheet).flushRows();
}
}
((SXSSFSheet)sheet).flushRows();
}
/**
*
*
* @throws IOException
*/
public void exportAllData(String[] columns, String[] headers, List<ProjectUserTime> staff, List<ProjectUserTime> project, Map<String, BigDecimal> map, int rowIndex) throws IOException{
// 遍历集合数据,产生数据行
int index = rowIndex;
Font font3 = workbook.createFont();
font3.setColor(HSSFColor.HSSFColorPredefined.BLUE.getIndex());
Row row = sheet.createRow(index++);
Cell first = row.createCell(0);
first.setCellStyle(style2);
first.setCellType(CellType.STRING);
first.setCellValue(project.get(rowIndex - 1).getProjectName());
Cell second = row.createCell(1);
second.setCellStyle(style2);
second.setCellType(CellType.NUMERIC);
second.setCellValue(project.get(rowIndex - 1).getCostSum().doubleValue());
Cell third = row.createCell(2);
third.setCellStyle(style2);
third.setCellType(CellType.NUMERIC);
third.setCellValue(project.get(rowIndex - 1).getSalarySum().doubleValue());
for(short col = 3; col < headers.length; col++){
Cell cell = row.createCell(col);
cell.setCellStyle(style2);
Object value = map.getOrDefault(project.get(rowIndex - 1).getProjectName() + staff.get(col - 3).getUserId(), new BigDecimal(0));
cell.setCellType(CellType.STRING);
if(value == null){
cell.setCellType(CellType.STRING);
cell.setCellValue("");
} else if(value instanceof BigDecimal){
cell.setCellType(CellType.NUMERIC);
cell.setCellValue(((BigDecimal)value).doubleValue());
} else{
cell.setCellType(CellType.STRING);
cell.setCellValue(value.toString());
}
}
((SXSSFSheet)sheet).flushRows();
}
/**
* Excel
*/
public static <T> ExportUtils exportHumanCost(String[] headers , String[] columns , List<ProjectUserTime> staff, List<ProjectUserTime> project, OutputStream outputStream , Map<String, BigDecimal> map) throws IOException{
ExportUtils exportUtils = new ExportUtils(headers);
int rowIndex = 1;
while(rowIndex <= columns.length){
exportUtils.exportAllData(columns, headers, staff, project, map, rowIndex);
rowIndex++;
}
if( null!= outputStream) {
exportUtils.write(outputStream);
}
return exportUtils;
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #eee;border-left-width:6px;background-color:#FAFAFA;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:40px;line-height:40px;border-bottom:1px solid #eee}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 10px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view .layui-code-ol li:first-child{padding-top:10px}.layui-code-view .layui-code-ol li:last-child{padding-bottom:10px}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}.layui-code-demo .layui-code{visibility:visible!important;margin:-15px;border-top:none;border-right:none;border-bottom:none}.layui-code-demo .layui-tab-content{padding:15px;border-top:none}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 299 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,170 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<link rel="stylesheet" href="../assets/css/amazeui.switch.css"/>
<div class="admin-content">
<div class="am-cf am-padding" style="padding:1rem 1.6rem 1.6rem 1rem;margin:0px;">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">人力成本管理</strong> /
<small>人力成本管理</small>
</div>
</div>
<div class="am-g">
<div class="am-u-sm-12">
<form class="am-form" id="listForm" action="${base}/humanCost/list" method="POST">
<input type="hidden" id="keywords" name="keywords" value='${keywords!""}'/>
<table class="am-table am-table-bordered am-table-radius table-main" style="padding:0;">
<tbody>
<tr>
<th class="am-text-middle">项目时间</th>
<td>
<div class="am-u-sm-10">
<div class="am-form am-form-inline">
<div class="am-form-group am-form-icon">
<input type="text" id="time" autocomplete="off" value="${time!}">
</div>
</div>
</div>
</td>
<td colspan="2">
<div align='right'>
<#-- <@shiro.hasPermission name="HUMAN_QUERY">-->
<button type="button" class="am-btn am-btn-default am-btn-sm am-text-secondary"
id="submit-btn">搜索
</button>
<#-- </@shiro.hasPermission>-->
<#-- <@shiro.hasPermission name="HUMAN_EXPORT">-->
<button type="button" class="am-btn am-btn-default am-btn-sm am-text-secondary"
id="submit-btn-export">导出
</button>
<#-- </@shiro.hasPermission>-->
</div>
</td>
</tr>
</tbody>
</table>
</form>
</div>
<div class="am-u-sm-12 am-u-md-12" style="padding:0 1.6rem 1.6rem 1rem;margin:0;">
<div class="am-btn-toolbar" style="padding-left:.5rem;">
<div class="am-btn-group am-btn-group-xs">
<@shiro.hasPermission name="HUMAN_IMPORT">
</@shiro.hasPermission>
</div>
</div>
</div>
</div>
<div class="am-g">
<div class="am-u-sm-12">
<div class="am-scrollable-horizontal">
<table class="am-table am-table-striped am-table-hover table-main">
<thead>
<tr class="am-text-nowrap">
<th class="table-title">项目名称</th>
<th class="table-title">人力总计</th>
<th class="table-title">${individualTime!}</th>
</tr>
</thead>
<tbody>
<#list pager.list as list>
<tr>
<td>${list.projectName!}</td>
<td>${(list.userCost!0)?string("0.##")}</td>
<td>${(list.userCost!0)?string("0.##")}</td>
</tr>
</#list>
</tbody>
</table>
</div>
<div class="am-cf">
<!-- 分页 -->
<#if (pager.list)?exists && (pager.list?size>0) >
<div class="am-fr">
<#include "../common/common_pager.ftl">
</div>
<#else>
<div class="am-kai" align="center">
<h3>没有找到任何记录!</h3>
</div>
</#if>
</div>
</div>
</div>
<footer class="admin-content-footer">
<hr>
</footer>
</div>
</@defaultLayout.layout>
<script src="${base}/layui/layui.js"></script>
<script src="../assets/js/amazeui.switch.js"></script>
<script type="text/javascript">
layui.use('laydate', function(){
var laydate = layui.laydate;
laydate.render({
elem: '#time',
type: 'month',
btns: ['confirm'],
trigger: 'click',
ready: function(date){
console.log(date);
}
});
});
$(function () {
$("#submit-btn").on("click", function () {
$("#pageNumber").val(1);
setKeywords();
$("#listForm").attr("action","${base}/humanCost/list");
$("#listForm").submit();
});
$("#submit-btn-export").on("click", function () {
setKeywords();
$("#listForm").attr("action","${base}/humanCost/export");
$("#listForm").submit();
});
function setKeywords() {
var keywordsObj = {};
if ($("#time").val())
keywordsObj.time = $("#time").val();
var keywords = "";
if (!$.isEmptyObject(keywordsObj)) {
keywords = JSON.stringify(keywordsObj);
}
console.log("keywords = " + keywords);
$("#keywords").val(keywords);
}
});
</script>

View File

@ -0,0 +1,318 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<link rel="stylesheet" href="../assets/css/amazeui.switch.css"/>
<div class="admin-content">
<div class="am-cf am-padding" style="padding:1rem 1.6rem 1.6rem 1rem;margin:0px;">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">人力成本管理</strong> /
<small>人力成本管理</small>
</div>
</div>
<div class="am-g">
<div class="am-u-sm-12">
<form class="am-form" id="listForm" action="${base}/humanCost/list" method="POST">
<input type="hidden" id="keywords" name="keywords" value='${keywords!""}'/>
<table class="am-table am-table-bordered am-table-radius table-main" style="padding:0;">
<tbody>
<tr>
<th class="am-text-middle">项目名称</th>
<td>
<div class="am-u-sm-10">
<select data-am-selected id="projectId" name="projectId">
<option value="-1">全部</option>
<#list projectList as project>
<option value=${project.id!} <#if projectId! =="${project.id}" >
selected
</#if>>${project.name!}</option>
</#list>
</select>
</div>
</td>
<th class="am-text-middle">姓名</th>
<td colspan="4">
<div class="am-u-sm-10">
<input type="text" id="userName" class="am-form-field am-input-sm"
value="${userName!}"/>
</div>
</td>
</tr>
<#if deptVary gt 0>
<tr>
<th class="am-text-middle">一级部门</th>
<td>
<div class="am-u-sm-10">
<select data-am-selected id="deptId" name="deptId">
<option value="-1">全部</option>
<#list deptList as dept>
<option value=${dept.id!} <#if deptId! =="${dept.id}" >
selected
</#if>>${dept.name!}</option>
</#list>
</select>
</div>
</td>
<th class="am-text-middle">项目时间</th>
<td>
<div class="am-u-sm-10">
<div class="am-form am-form-inline">
<div class="am-form-group am-form-icon">
<input type="text" id="time" autocomplete="off" value="${time!}">
</div>
</div>
</div>
</td>
<td colspan="2">
<div align='right'>
<#-- <@shiro.hasPermission name="HUMAN_COST_QUERY">-->
<button type="button" class="am-btn am-btn-default am-btn-sm am-text-secondary"
id="submit-btn">搜索
</button>
<#-- </@shiro.hasPermission>-->
<#-- <@shiro.hasPermission name="HUMAN_COST_QUERY">-->
<button type="button" class="am-btn am-btn-default am-btn-sm am-text-secondary"
id="submit-btn-export">导出
</button>
<#-- </@shiro.hasPermission>-->
</div>
</td>
</tr>
<#else>
<tr>
<th class="am-text-middle">项目时间</th>
<td>
<div class="am-u-sm-10">
<div class="am-form am-form-inline">
<div class="am-form-group am-form-icon">
<input type="text" id="time" autocomplete="off" value="${time!}">
</div>
</div>
</div>
</td>
<td colspan="4">
<div align='right'>
<#-- <@shiro.hasPermission name="HUMAN_COST_QUERY">-->
<button type="button" class="am-btn am-btn-default am-btn-sm am-text-secondary"
id="submit-btn">搜索
</button>
<#-- </@shiro.hasPermission>-->
<#-- <@shiro.hasPermission name="HUMAN_COST_QUERY">-->
<button type="button" class="am-btn am-btn-default am-btn-sm am-text-secondary"
id="submit-btn-export">导出
</button>
<#-- </@shiro.hasPermission>-->
</div>
</td>
</tr>
</#if>
</tbody>
</table>
</form>
</div>
<div class="am-u-sm-12 am-u-md-12" style="padding:0 1.6rem 1.6rem 1rem;margin:0;">
<div class="am-btn-toolbar" style="padding-left:.5rem;">
<div class="am-btn-group am-btn-group-xs">
<#-- <@shiro.hasPermission name="HUMAN_COST_IMPORT">-->
<div class="am-btn-group am-btn-group-xs am-form-file">
<button type="button" id="importAccount" class="am-btn am-btn-default"><span
class="am-icon-archive"></span> 批量导入
</button>
<input id="doc-form-file" type="file" name="file"
onChange="ajaxUploadFile('doc-form-file','${base}/humanCost/batchImport')"
multiple>
</div>
<button type="button" id="importTemplate" class="am-btn am-btn-default" >
<span class="am-icon-arrow-circle-down"></span> 导入模板
</button>
<#-- </@shiro.hasPermission>-->
</div>
</div>
</div>
</div>
<div class="am-g">
<div class="am-u-sm-12">
<div class="am-scrollable-horizontal">
<table class="am-table am-table-striped am-table-hover table-main">
<thead>
<tr class="am-text-nowrap">
<th class="table-title">项目名称</th>
<th class="table-title">人力总计</th>
<th class="table-title">成本总计</th>
<#if (staff)?exists>
<#list staff as person>
<th class="table-title">${person.userName!}
<#if showSalary gt 0>(成本:${(person.userSalary!0)?string("0.##")}元)</#if></th>
</#list>
</#if>
</tr>
</thead>
<tbody>
<#if (pager.list)?exists>
<#list pager.list as list>
<tr>
<td>${list.projectName!}</td>
<td>${(list.costSum!0)?string("0.##")}</td>
<td>${(list.salarySum!0)?string("0.##")}</td>
<#if (staff)?exists>
<#list staff as person>
<td>${((map[list.projectName + person.userId])!0)?string("0.##")}</td>
</#list>
</#if>
</tr>
</#list>
</#if>
</tbody>
</table>
</div>
<div class="am-cf">
<!-- 分页 -->
<#if (pager.list)?exists && (pager.list?size>0) >
<div class="am-fr">
<#include "../common/common_pager.ftl">
</div>
<#else>
<div class="am-kai" align="center">
<h3>没有找到任何记录!</h3>
</div>
</#if>
</div>
</div>
</div>
<footer class="admin-content-footer">
<hr>
</footer>
</div>
</@defaultLayout.layout>
<script src="../assets/js/jquery.ajaxfileupload.js"></script>
<script src="../assets/js/amazeui.switch.js"></script>
<script src="${base}/layui/layui.js"></script>
<script type="text/javascript">
layui.use('laydate', function(){
var laydate = layui.laydate;
laydate.render({
elem: '#time',
type: 'month',
btns: ['confirm'],
trigger: 'click',
ready: function(date){
console.log(date);
}
});
});
/**
*导入模板下载
*/
$("#importTemplate").on('click', function(event) {
location.href = "${base}/humanCost/template";
});
function ajaxUploadFile(id, url) {
// if ($('#modal')) {
// $('#modal').modal('open');
// $('#span-' + id).html("&nbsp;&nbsp;&nbsp;&nbsp;数据正在导入,请等待");
// }
$("#exportFaildButton").hide();
$.ajaxFileUpload({
url: url,
secureuri: false,
fileElementId: id,// file标签的id
dataType: 'json',// 返回数据的类型
success: function (data, status) {
console.log("--------success---------" + data)
// if($('#modal')){
// $('#modal').modal('close');
// $('#span-'+id).html("导入完成");
// }
if(data.status ==0) {
var list = data.data;
var content = '';
$.each(list, function (i, r) {
content += (i+1) + '、' + r + '<br>';
});
console.log('---> ' + content);
parent.layer.open({
title: '导入结果:' ,
content: data.msg + '<br><br>' + content
});
}else{
parent.layer.msg(data.msg);
}
window.location.reload();
$("#" + id).val("");
},
error: function (data, status, e) {
console.log("--------error---------" + data)
alert("-----------------" + data);
if ($('#modal')) {
$('#modal').modal('close');
}
alert(e);
$("#" + id).val("");
}
});
};
$(function () {
$("#submit-btn").on("click", function () {
$("#pageNumber").val(1);
setKeywords();
$("#listForm").attr("action","${base}/humanCost/list");
$("#listForm").submit();
});
$("#submit-btn-export").on("click", function () {
setKeywords();
$("#listForm").attr("action","${base}/humanCost/export");
$("#listForm").submit();
});
function setKeywords() {
var keywordsObj = {};
if ($("#projectId").val())
keywordsObj.projectId = $("#projectId").val();
if ($("#userName").val())
keywordsObj.userName = $("#userName").val();
if ($("#deptId").val())
keywordsObj.deptId = $("#deptId").val();
if ($("#time").val())
keywordsObj.time = $("#time").val();
var keywords = "";
if (!$.isEmptyObject(keywordsObj)) {
keywords = JSON.stringify(keywordsObj);
}
console.log("keywords = " + keywords);
$("#keywords").val(keywords);
}
});
</script>