운영계획서 페이지 파일 업로드, 다운로드, 삭제 기능 추가.
parent
50b9bdc061
commit
ada59385fe
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.dbnt.faisp.config;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class BaseService {
|
||||||
|
|
||||||
|
@Value("${spring.servlet.multipart.location}")
|
||||||
|
protected String locationPath;
|
||||||
|
|
||||||
|
protected String calculationSize(double fileSize){
|
||||||
|
String[] units = {"bytes", "KB", "MB", "GB", "TB", "PB"};
|
||||||
|
double unitSelector = Math.floor(Math.log(fileSize)/Math.log(1024));
|
||||||
|
if(fileSize>0){
|
||||||
|
return Math.round((fileSize/Math.pow(1024, unitSelector))*100)/100d+" "+units[(int)unitSelector];
|
||||||
|
}else{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteStoredFile(File deleteFile){
|
||||||
|
deleteFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
package com.dbnt.faisp.config;
|
||||||
|
|
||||||
|
import com.dbnt.faisp.fpiMgt.monthPlan.service.MonthPlanService;
|
||||||
|
import com.dbnt.faisp.userInfo.model.UserInfo;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FileController {
|
||||||
|
|
||||||
|
private final MonthPlanService monthPlanService;
|
||||||
|
|
||||||
|
@GetMapping("/file/fileDownload")
|
||||||
|
public void fileDownload(HttpServletRequest request,
|
||||||
|
HttpServletResponse response,
|
||||||
|
String board,
|
||||||
|
Integer parentKey,
|
||||||
|
Integer fileSeq) {
|
||||||
|
FileInfo downloadFile = null;
|
||||||
|
switch (board){
|
||||||
|
case "monthPlan":
|
||||||
|
downloadFile = monthPlanService.selectPlanFile(parentKey, fileSeq);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedInputStream in;
|
||||||
|
BufferedOutputStream out;
|
||||||
|
try {
|
||||||
|
File file = new File(downloadFile.getSavePath(), downloadFile.getConvNm());
|
||||||
|
|
||||||
|
setDisposition(downloadFile.getFullName(), request, response);
|
||||||
|
in = new BufferedInputStream(new FileInputStream(file));
|
||||||
|
out = new BufferedOutputStream(response.getOutputStream());
|
||||||
|
FileCopyUtils.copy(in, out);
|
||||||
|
out.flush();
|
||||||
|
if(out!=null) out.close();
|
||||||
|
if(in!=null )in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void setDisposition(String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
|
String browser = getBrowser(request);
|
||||||
|
|
||||||
|
String dispositionPrefix = "attachment; filename=";
|
||||||
|
String encodedFilename = null;
|
||||||
|
|
||||||
|
if (browser.equals("MSIE")) {
|
||||||
|
encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
|
||||||
|
} else if (browser.equals("Trident")) { // IE11 문자열 깨짐 방지
|
||||||
|
encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
|
||||||
|
} else if (browser.equals("Firefox")) {
|
||||||
|
encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
|
||||||
|
} else if (browser.equals("Opera")) {
|
||||||
|
encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
|
||||||
|
} else if (browser.equals("Chrome")) {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int i = 0; i < filename.length(); i++) {
|
||||||
|
char c = filename.charAt(i);
|
||||||
|
if (c > '~') {
|
||||||
|
sb.append(URLEncoder.encode("" + c, "UTF-8"));
|
||||||
|
} else {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encodedFilename = sb.toString();
|
||||||
|
} else {
|
||||||
|
throw new IOException("Not supported browser");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setHeader("Content-Disposition", dispositionPrefix + encodedFilename);
|
||||||
|
|
||||||
|
if ("Opera".equals(browser)) {
|
||||||
|
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBrowser(HttpServletRequest request) {
|
||||||
|
String header = request.getHeader("User-Agent");
|
||||||
|
if (header.indexOf("MSIE") > -1) {
|
||||||
|
return "MSIE";
|
||||||
|
} else if (header.indexOf("Trident") > -1) { // IE11 문자열 깨짐 방지
|
||||||
|
return "Trident";
|
||||||
|
} else if (header.indexOf("Chrome") > -1) {
|
||||||
|
return "Chrome";
|
||||||
|
} else if (header.indexOf("Opera") > -1) {
|
||||||
|
return "Opera";
|
||||||
|
}
|
||||||
|
return "Firefox";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.dbnt.faisp.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Transient;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class FileInfo {
|
||||||
|
|
||||||
|
private String origNm;
|
||||||
|
private String convNm;
|
||||||
|
private String fileExtn;
|
||||||
|
private String fileSize;
|
||||||
|
private String savePath;
|
||||||
|
|
||||||
|
public String getFullName(){
|
||||||
|
return getOrigNm()+"."+getFileExtn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import com.dbnt.faisp.userInfo.model.UserInfo;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
@ -55,7 +56,10 @@ public class FpiMgtController { // 외사경찰견문관리
|
||||||
@PostMapping("/savePlan")
|
@PostMapping("/savePlan")
|
||||||
public Integer savePlan(BoardPlan boardPlan,
|
public Integer savePlan(BoardPlan boardPlan,
|
||||||
@RequestParam(value = "planInfos", required = false) List<String> planInfos,
|
@RequestParam(value = "planInfos", required = false) List<String> planInfos,
|
||||||
@RequestParam(value = "detailPlanInfos", required = false)List<String> detailPlanInfos){
|
@RequestParam(value = "detailPlanInfos", required = false)List<String> detailPlanInfos,
|
||||||
return monthPlanService.saveBoardPlan(boardPlan, planInfos, detailPlanInfos);
|
MultipartHttpServletRequest request,
|
||||||
|
@RequestParam(value = "fileSeq", required = false) List<Integer> deleteFileSeq){
|
||||||
|
boardPlan.setMultipartFileList(request.getMultiFileMap().get("uploadFiles"));
|
||||||
|
return monthPlanService.saveBoardPlan(boardPlan, planInfos, detailPlanInfos, deleteFileSeq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import lombok.Setter;
|
||||||
import org.hibernate.annotations.DynamicInsert;
|
import org.hibernate.annotations.DynamicInsert;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
|
@ -52,9 +53,13 @@ public class BoardPlan extends BaseModel {
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
|
||||||
private LocalDateTime wrtDt;
|
private LocalDateTime wrtDt;
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
private Integer fileCnt;
|
||||||
@Transient
|
@Transient
|
||||||
private List<PlanMainInfo> mainInfoList;
|
private List<PlanMainInfo> mainInfoList;
|
||||||
@Transient
|
@Transient
|
||||||
private List<PlanFile> fileList;
|
private List<PlanFile> fileList;
|
||||||
|
@Transient
|
||||||
|
private List<MultipartFile> multipartFileList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.dbnt.faisp.fpiMgt.monthPlan.model;
|
package com.dbnt.faisp.fpiMgt.monthPlan.model;
|
||||||
|
|
||||||
|
import com.dbnt.faisp.config.FileInfo;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import org.hibernate.annotations.DynamicInsert;
|
import org.hibernate.annotations.DynamicInsert;
|
||||||
import org.hibernate.annotations.DynamicUpdate;
|
import org.hibernate.annotations.DynamicUpdate;
|
||||||
|
|
@ -15,7 +16,7 @@ import java.io.Serializable;
|
||||||
@DynamicUpdate
|
@DynamicUpdate
|
||||||
@Table(name = "plan_file")
|
@Table(name = "plan_file")
|
||||||
@IdClass(PlanFile.PlanFileId.class)
|
@IdClass(PlanFile.PlanFileId.class)
|
||||||
public class PlanFile{
|
public class PlanFile extends FileInfo {
|
||||||
@Id
|
@Id
|
||||||
@Column(name = "plan_key")
|
@Column(name = "plan_key")
|
||||||
private Integer planKey;
|
private Integer planKey;
|
||||||
|
|
@ -29,7 +30,7 @@ public class PlanFile{
|
||||||
@Column(name = "file_extn")
|
@Column(name = "file_extn")
|
||||||
private String fileExtn;
|
private String fileExtn;
|
||||||
@Column(name = "file_size")
|
@Column(name = "file_size")
|
||||||
private Integer fileSize;
|
private String fileSize;
|
||||||
@Column(name = "save_path")
|
@Column(name = "save_path")
|
||||||
private String savePath;
|
private String savePath;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ import com.dbnt.faisp.fpiMgt.monthPlan.model.PlanFile;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
||||||
public interface PlanFileRepository extends JpaRepository<PlanFile, PlanFile.PlanFileId> {
|
public interface PlanFileRepository extends JpaRepository<PlanFile, PlanFile.PlanFileId> {
|
||||||
List<PlanFile> findByPlanKey(Integer planKey);
|
List<PlanFile> findByPlanKey(Integer planKey);
|
||||||
|
Optional<PlanFile> findByPlanKeyOrderByFileSeqDesc(Integer planKey);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.dbnt.faisp.fpiMgt.monthPlan.service;
|
package com.dbnt.faisp.fpiMgt.monthPlan.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.dbnt.faisp.config.BaseService;
|
||||||
|
import com.dbnt.faisp.config.FileInfo;
|
||||||
import com.dbnt.faisp.fpiMgt.monthPlan.mapper.MonthPlanMapper;
|
import com.dbnt.faisp.fpiMgt.monthPlan.mapper.MonthPlanMapper;
|
||||||
import com.dbnt.faisp.fpiMgt.monthPlan.model.BoardPlan;
|
import com.dbnt.faisp.fpiMgt.monthPlan.model.BoardPlan;
|
||||||
import com.dbnt.faisp.fpiMgt.monthPlan.model.PlanFile;
|
import com.dbnt.faisp.fpiMgt.monthPlan.model.PlanFile;
|
||||||
|
|
@ -9,15 +11,20 @@ import com.dbnt.faisp.fpiMgt.monthPlan.repository.BoardPlanRepository;
|
||||||
import com.dbnt.faisp.fpiMgt.monthPlan.repository.PlanFileRepository;
|
import com.dbnt.faisp.fpiMgt.monthPlan.repository.PlanFileRepository;
|
||||||
import com.dbnt.faisp.fpiMgt.monthPlan.repository.PlanMainInfoRepository;
|
import com.dbnt.faisp.fpiMgt.monthPlan.repository.PlanMainInfoRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class MonthPlanService {
|
public class MonthPlanService extends BaseService {
|
||||||
private final BoardPlanRepository boardPlanRepository;
|
private final BoardPlanRepository boardPlanRepository;
|
||||||
private final PlanFileRepository planFileRepository;
|
private final PlanFileRepository planFileRepository;
|
||||||
private final PlanMainInfoRepository planMainInfoRepository;
|
private final PlanMainInfoRepository planMainInfoRepository;
|
||||||
|
|
@ -25,16 +32,24 @@ public class MonthPlanService {
|
||||||
|
|
||||||
public BoardPlan selectBoardPlan(Integer planKey) {
|
public BoardPlan selectBoardPlan(Integer planKey) {
|
||||||
BoardPlan savedPlan = boardPlanRepository.findById(planKey).orElse(null);
|
BoardPlan savedPlan = boardPlanRepository.findById(planKey).orElse(null);
|
||||||
|
if (savedPlan != null) {
|
||||||
savedPlan.setFileList(planFileRepository.findByPlanKey(planKey));
|
savedPlan.setFileList(planFileRepository.findByPlanKey(planKey));
|
||||||
savedPlan.setMainInfoList(planMainInfoRepository.findByPlanKey(planKey));
|
savedPlan.setMainInfoList(planMainInfoRepository.findByPlanKey(planKey));
|
||||||
|
}
|
||||||
return savedPlan;
|
return savedPlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public Integer saveBoardPlan(BoardPlan boardPlan, List<String> planInfos, List<String> detailPlanInfos) {
|
public Integer saveBoardPlan(BoardPlan boardPlan, List<String> planInfos, List<String> detailPlanInfos, List<Integer> deleteFileSeq) {
|
||||||
Integer planKey = boardPlanRepository.save(boardPlan).getPlanKey();
|
Integer planKey = boardPlanRepository.save(boardPlan).getPlanKey();
|
||||||
Integer infoSeq = savePlanMainInfos(planKey,0, "S", planInfos);//요약 summery
|
Integer infoSeq = savePlanMainInfos(planKey,0, "S", planInfos);//요약 summery
|
||||||
savePlanMainInfos(planKey, infoSeq, "D", detailPlanInfos);//상세 detail
|
savePlanMainInfos(planKey, infoSeq, "D", detailPlanInfos);//상세 detail
|
||||||
|
if(deleteFileSeq.size()>0){
|
||||||
|
deletePlanFile(planKey, deleteFileSeq);
|
||||||
|
}
|
||||||
|
if(boardPlan.getMultipartFileList()!=null){
|
||||||
|
saveUploadFiles(planKey, boardPlan.getMultipartFileList());
|
||||||
|
}
|
||||||
return planKey;
|
return planKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,4 +72,57 @@ public class MonthPlanService {
|
||||||
public Integer selectBoardPlanListCnt(BoardPlan boardPlan) {
|
public Integer selectBoardPlanListCnt(BoardPlan boardPlan) {
|
||||||
return monthPlanMapper.selectBoardPlanListCnt(boardPlan);
|
return monthPlanMapper.selectBoardPlanListCnt(boardPlan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveUploadFiles(Integer planKey, List<MultipartFile> multipartFileList){
|
||||||
|
PlanFile lastFileInfo = planFileRepository.findByPlanKeyOrderByFileSeqDesc(planKey).orElse(null);
|
||||||
|
int fileSeq = lastFileInfo==null?1:(lastFileInfo.getFileSeq()+1);
|
||||||
|
for(MultipartFile file : multipartFileList){
|
||||||
|
String saveName = UUID.randomUUID().toString();
|
||||||
|
String path = locationPath+File.separator+"monthPlan"+File.separator;
|
||||||
|
|
||||||
|
File saveFile = new File(path+File.separator+saveName);
|
||||||
|
if(file.getSize()!=0){ // 저장될 파일 확인
|
||||||
|
if(!saveFile.exists()){ // 저장될 경로 확인
|
||||||
|
if(saveFile.getParentFile().mkdirs()){
|
||||||
|
try{
|
||||||
|
saveFile.createNewFile();
|
||||||
|
}catch (IOException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
file.transferTo(saveFile);
|
||||||
|
}catch (IllegalStateException | IOException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
int extnIdx = originalFilename.lastIndexOf(".");
|
||||||
|
PlanFile fileInfo = new PlanFile();
|
||||||
|
fileInfo.setPlanKey(planKey);
|
||||||
|
fileInfo.setFileSeq(fileSeq++);
|
||||||
|
fileInfo.setOrigNm(originalFilename.substring(0, extnIdx));
|
||||||
|
fileInfo.setFileExtn(originalFilename.substring(extnIdx+1));
|
||||||
|
fileInfo.setConvNm(saveName);
|
||||||
|
fileInfo.setFileSize(calculationSize(file.getSize()));
|
||||||
|
fileInfo.setSavePath(path);
|
||||||
|
planFileRepository.save(fileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePlanFile(Integer planKey, List<Integer> deleteFileSeq) {
|
||||||
|
List<PlanFile> planFileList = planFileRepository.findByPlanKey(planKey);
|
||||||
|
for(PlanFile file: planFileList){
|
||||||
|
if(deleteFileSeq.contains(file.getFileSeq())){
|
||||||
|
deleteStoredFile(new File(file.getSavePath(), file.getConvNm()));
|
||||||
|
planFileRepository.delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileInfo selectPlanFile(Integer parentKey, Integer fileSeq) {
|
||||||
|
return planFileRepository.findById(new PlanFile.PlanFileId(parentKey, fileSeq)).orElse(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
a.wrt_organ,
|
a.wrt_organ,
|
||||||
a.wrt_nm,
|
a.wrt_nm,
|
||||||
a.wrt_dt,
|
a.wrt_dt,
|
||||||
b.file_seq
|
b.fileCnt
|
||||||
from board_plan a
|
from board_plan a
|
||||||
left outer join (select plan_key,
|
left outer join (select plan_key,
|
||||||
max(file_seq) as file_seq
|
count(file_seq) as fileCnt
|
||||||
from plan_file
|
from plan_file
|
||||||
group by plan_key) b
|
group by plan_key) b
|
||||||
on a.plan_key = b.plan_key
|
on a.plan_key = b.plan_key
|
||||||
|
|
|
||||||
|
|
@ -46,3 +46,46 @@ $(document).on('mouseleave', '.menuDiv', function (){
|
||||||
$(".secondMenu").hide();
|
$(".secondMenu").hide();
|
||||||
$(".thirdMenu").hide();
|
$(".thirdMenu").hide();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$(document).on('change', '#fileInputer', function (){
|
||||||
|
for(const file of this.files){
|
||||||
|
setFileDiv(file, files.push(file));
|
||||||
|
}
|
||||||
|
this.value = null;
|
||||||
|
})
|
||||||
|
|
||||||
|
function byteCalculation(size) {
|
||||||
|
const bytes = parseInt(size);
|
||||||
|
const s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
||||||
|
const e = Math.floor(Math.log(bytes)/Math.log(1024));
|
||||||
|
|
||||||
|
if(e === "-Infinity") return "0 "+s[0];
|
||||||
|
else return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('click', '.fileDelete', function (){
|
||||||
|
const target = $(this);
|
||||||
|
files[Number(target.attr("data-fileidx"))].isDelete = true;
|
||||||
|
target.parent().remove();
|
||||||
|
const uploadDiv = $("#uploadDiv");
|
||||||
|
if(uploadDiv.children().length === 0){
|
||||||
|
uploadDiv.append("<br>파일을 업로드 해주세요.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).on('click', '.uploadedFileDelete', function (){
|
||||||
|
const target = $(this).parent().find("span")[0];
|
||||||
|
if(target.className===""){
|
||||||
|
target.className = "text-decoration-line-through";
|
||||||
|
}else{
|
||||||
|
target.className = "";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$(document).on('click', '.fileDownLink', function (){
|
||||||
|
let url = "/file/fileDownload?"
|
||||||
|
url += "board="+$(this).attr("data-board");
|
||||||
|
url += "&parentKey="+Number($("#viewModalPlanKey").val());
|
||||||
|
url += "&fileSeq="+$(this).attr("data-fileseq");
|
||||||
|
window.open(encodeURI(url));
|
||||||
|
})
|
||||||
|
|
@ -1,22 +1,11 @@
|
||||||
|
let files = [];
|
||||||
|
|
||||||
$(document).on('click', '#addPlanBtn', function (){
|
$(document).on('click', '#addPlanBtn', function (){
|
||||||
getEditModal(null)
|
getEditModal(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
$(document).on('click', '.planTr', function (){
|
$(document).on('click', '.planTr', function (){
|
||||||
$.ajax({
|
getViewModal(Number($(this).find(".planKey").val()));
|
||||||
url: '/fpiMgt/planViewModal',
|
|
||||||
data: {planKey: Number($(this).find(".planKey").val())},
|
|
||||||
type: 'GET',
|
|
||||||
dataType:"html",
|
|
||||||
success: function(html){
|
|
||||||
$("#planViewBody").empty().append(html)
|
|
||||||
$("#planViewModal").modal('show');
|
|
||||||
},
|
|
||||||
error:function(){
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
$(document).on('click', '#editPlanBtn', function (){
|
$(document).on('click', '#editPlanBtn', function (){
|
||||||
$("#planViewModal").modal('hide');
|
$("#planViewModal").modal('hide');
|
||||||
|
|
@ -52,6 +41,22 @@ $(document).on('click', '#saveTempBtn', function (){
|
||||||
savePlan('T')
|
savePlan('T')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function getViewModal(planKey){
|
||||||
|
$.ajax({
|
||||||
|
url: '/fpiMgt/planViewModal',
|
||||||
|
data: {planKey: planKey},
|
||||||
|
type: 'GET',
|
||||||
|
dataType:"html",
|
||||||
|
success: function(html){
|
||||||
|
$("#planViewBody").empty().append(html)
|
||||||
|
$("#planViewModal").modal('show');
|
||||||
|
},
|
||||||
|
error:function(){
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getEditModal(planKey){
|
function getEditModal(planKey){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/fpiMgt/planEditModal',
|
url: '/fpiMgt/planEditModal',
|
||||||
|
|
@ -77,18 +82,47 @@ function getEditModal(planKey){
|
||||||
['table', ['table']]
|
['table', ['table']]
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
setUploadDiv();
|
||||||
},
|
},
|
||||||
error:function(){
|
error:function(){
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function setUploadDiv(){
|
||||||
|
files = [];
|
||||||
|
$("#uploadDiv").on("dragenter", function(e) {
|
||||||
|
// $(this).addClass('drag-over');
|
||||||
|
}).on("dragleave", function(e) {
|
||||||
|
// $(this).removeClass('drag-over');
|
||||||
|
}).on("dragover", function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}).on('drop', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
// $(this).removeClass('drag-over');
|
||||||
|
for(const file of e.originalEvent.dataTransfer.files){
|
||||||
|
setFileDiv(file, files.push(file));
|
||||||
|
}
|
||||||
|
}).on('click', function (e){
|
||||||
|
if(e.target.className.indexOf("ileDelete")<0){
|
||||||
|
$("#fileInputer").click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
function savePlan(planState){
|
function savePlan(planState){
|
||||||
|
if(contentCheck()){
|
||||||
if(confirm("저장하시겠습니까?")){
|
if(confirm("저장하시겠습니까?")){
|
||||||
$("#planState").val(planState);
|
$("#planState").val(planState);
|
||||||
contentFade("in");
|
contentFade("in");
|
||||||
const formData = new FormData($("#planEditForm")[0]);
|
const formData = new FormData($("#planEditForm")[0]);
|
||||||
|
for(const file of files) {
|
||||||
|
if(!file.isDelete)
|
||||||
|
formData.append('uploadFiles', file, file.name);
|
||||||
|
}
|
||||||
|
$(".text-decoration-line-through").each(function (idx, el){
|
||||||
|
formData.append('fileSeq', $(el).attr("data-fileseq"));
|
||||||
|
})
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type : 'POST',
|
type : 'POST',
|
||||||
data : formData,
|
data : formData,
|
||||||
|
|
@ -96,8 +130,10 @@ function savePlan(planState){
|
||||||
processData: false,
|
processData: false,
|
||||||
contentType: false,
|
contentType: false,
|
||||||
success : function(result) {
|
success : function(result) {
|
||||||
debugger
|
alert("저장되었습니다.");
|
||||||
contentFade("out");
|
contentFade("out");
|
||||||
|
$("#planEditModal").modal('hide');
|
||||||
|
getViewModal(result);
|
||||||
},
|
},
|
||||||
error : function(xhr, status) {
|
error : function(xhr, status) {
|
||||||
alert("저장에 실패하였습니다.")
|
alert("저장에 실패하였습니다.")
|
||||||
|
|
@ -105,4 +141,44 @@ function savePlan(planState){
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFileDiv(file, idx){
|
||||||
|
const uploadDiv = $("#uploadDiv");
|
||||||
|
if($(".uploadedFileDelete").length===0 && $(".fileDelete").length === 0){
|
||||||
|
uploadDiv.empty();
|
||||||
|
}
|
||||||
|
let fileInfo = "<div class='row-col-6'>";
|
||||||
|
fileInfo += file.name+" "+byteCalculation(file.size)+" ";
|
||||||
|
fileInfo += "<a href='#' class='fileDelete text-danger text-decoration-none' data-fileidx='"+(idx-1)+"'>삭제</a>";
|
||||||
|
fileInfo += "</div>";
|
||||||
|
uploadDiv.append(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
function contentCheck(){
|
||||||
|
let flag = true;
|
||||||
|
if(!$("#contentTitle").val()){
|
||||||
|
alert("제목을 입력해주세요.")
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
if(!$("#planDt").val()){
|
||||||
|
alert("시행일자를 입력해주세요.")
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
let totalSize = 0;
|
||||||
|
for(const file of files) {
|
||||||
|
if(!file.isDelete){
|
||||||
|
totalSize+=file.size;
|
||||||
|
if(file.size>209715200){
|
||||||
|
alert("파일당 사이즈는 200MB을 넘길 수 없습니다.")
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(totalSize>524288000){
|
||||||
|
alert("첨부파일의 용량 합은 500MB를 넘길 수 없습니다.")
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<form method="get" th:action="@{/fpiMgt/monthPlanPage}">
|
||||||
|
<input type="hidden" name="pageIndex" id="pageIndex" th:value="${searchParams.pageIndex}">
|
||||||
|
<div class="row justify-content-between pe-3 py-1">
|
||||||
|
<div class="col-auto">
|
||||||
|
<select class="form-select" name="rowCnt" id="rowCnt">
|
||||||
|
<th:block th:each="num : ${#numbers.sequence(1,5)}">
|
||||||
|
<option th:value="${num*10}" th:text="${num*10}" th:selected="${searchParams.rowCnt eq num*10}"></option>
|
||||||
|
</th:block>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="row justify-content-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="text" class="form-control form-control-sm">
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-sm btn-primary col-auto" id="searchBtn" value="검색">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="row justify-content-start">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>제목</th>
|
||||||
|
<th>시행일자</th>
|
||||||
|
<th>작성자</th>
|
||||||
|
<th>작성일시</th>
|
||||||
|
<th>첨부파일</th>
|
||||||
|
<th>상태</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="planTr" th:each="plan:${planList}">
|
||||||
|
<input type="hidden" class="planKey" th:value="${plan.planKey}">
|
||||||
|
<td><input type="checkbox"></td>
|
||||||
|
<td th:text="${plan.contentTitle}"></td>
|
||||||
|
<td th:text="${#temporals.format(plan.planDt, 'yyyy-MM-dd')}"></td>
|
||||||
|
<td th:text="${plan.wrtNm}"></td>
|
||||||
|
<td th:text="${#temporals.format(plan.wrtDt, 'yyyy-MM-dd HH:mm')}"></td>
|
||||||
|
<td></td>
|
||||||
|
<td th:if="${plan.sectionApprv eq 'T'}">계장결재</td>
|
||||||
|
<td th:if="${plan.sectionApprv eq 'F'}">계장반려</td>
|
||||||
|
<td th:if="${plan.headApprv eq 'T'}">부장결재</td>
|
||||||
|
<td th:if="${plan.headApprv eq 'F'}">부장반려</td>
|
||||||
|
<td th:if="${plan.planState eq 'T'}">임시저장</td>
|
||||||
|
<td th:if="${#strings.isEmpty(plan.sectionApprv) and #strings.isEmpty(plan.headApprv) and plan.planState eq 'S'}">결재대기</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-between">
|
||||||
|
<div class="col-auto"></div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination">
|
||||||
|
<th:block th:if="${searchParams.pageIndex>3}">
|
||||||
|
<li class="page-item" th:data-pageindex="${(searchParams.pageIndex)-3}">
|
||||||
|
<a class="page-link" href="#" aria-label="Previous">
|
||||||
|
<span aria-hidden="true">«</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:each="num : ${#numbers.sequence(searchParams.startNum, searchParams.endNum)}">
|
||||||
|
<li class="page-item" th:data-pageindex="${num}" th:classappend="${searchParams.pageIndex eq num?'active':''}">
|
||||||
|
<a class="page-link" href="#" th:text="${num}"></a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${searchParams.maxNum>searchParams.endNum+2}">
|
||||||
|
<li class="page-item" th:data-pageindex="${(searchParams.pageIndex)+3}">
|
||||||
|
<a class="page-link" href="#" aria-label="Next">
|
||||||
|
<span aria-hidden="true">»</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="button" class="btn btn-success" value="등록" id="addPlanBtn">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<form method="get" th:action="@{/fpiMgt/monthPlanPage}">
|
||||||
|
<input type="hidden" name="pageIndex" id="pageIndex" th:value="${searchParams.pageIndex}">
|
||||||
|
<div class="row justify-content-between pe-3 py-1">
|
||||||
|
<div class="col-auto">
|
||||||
|
<select class="form-select" name="rowCnt" id="rowCnt">
|
||||||
|
<th:block th:each="num : ${#numbers.sequence(1,5)}">
|
||||||
|
<option th:value="${num*10}" th:text="${num*10}" th:selected="${searchParams.rowCnt eq num*10}"></option>
|
||||||
|
</th:block>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="row justify-content-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="text" class="form-control form-control-sm">
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-sm btn-primary col-auto" id="searchBtn" value="검색">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="row justify-content-start">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>제목</th>
|
||||||
|
<th>시행일자</th>
|
||||||
|
<th>작성자</th>
|
||||||
|
<th>작성일시</th>
|
||||||
|
<th>첨부파일</th>
|
||||||
|
<th>상태</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="planTr" th:each="plan:${planList}">
|
||||||
|
<input type="hidden" class="planKey" th:value="${plan.planKey}">
|
||||||
|
<td><input type="checkbox"></td>
|
||||||
|
<td th:text="${plan.contentTitle}"></td>
|
||||||
|
<td th:text="${#temporals.format(plan.planDt, 'yyyy-MM-dd')}"></td>
|
||||||
|
<td th:text="${plan.wrtNm}"></td>
|
||||||
|
<td th:text="${#temporals.format(plan.wrtDt, 'yyyy-MM-dd HH:mm')}"></td>
|
||||||
|
<td></td>
|
||||||
|
<td th:if="${plan.sectionApprv eq 'T'}">계장결재</td>
|
||||||
|
<td th:if="${plan.sectionApprv eq 'F'}">계장반려</td>
|
||||||
|
<td th:if="${plan.headApprv eq 'T'}">부장결재</td>
|
||||||
|
<td th:if="${plan.headApprv eq 'F'}">부장반려</td>
|
||||||
|
<td th:if="${plan.planState eq 'T'}">임시저장</td>
|
||||||
|
<td th:if="${#strings.isEmpty(plan.sectionApprv) and #strings.isEmpty(plan.headApprv) and plan.planState eq 'S'}">결재대기</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-between">
|
||||||
|
<div class="col-auto"></div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination">
|
||||||
|
<th:block th:if="${searchParams.pageIndex>3}">
|
||||||
|
<li class="page-item" th:data-pageindex="${(searchParams.pageIndex)-3}">
|
||||||
|
<a class="page-link" href="#" aria-label="Previous">
|
||||||
|
<span aria-hidden="true">«</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:each="num : ${#numbers.sequence(searchParams.startNum, searchParams.endNum)}">
|
||||||
|
<li class="page-item" th:data-pageindex="${num}" th:classappend="${searchParams.pageIndex eq num?'active':''}">
|
||||||
|
<a class="page-link" href="#" th:text="${num}"></a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${searchParams.maxNum>searchParams.endNum+2}">
|
||||||
|
<li class="page-item" th:data-pageindex="${(searchParams.pageIndex)+3}">
|
||||||
|
<a class="page-link" href="#" aria-label="Next">
|
||||||
|
<span aria-hidden="true">»</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="button" class="btn btn-success" value="등록" id="addPlanBtn">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</html>
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
<td th:text="${#temporals.format(plan.planDt, 'yyyy-MM-dd')}"></td>
|
<td th:text="${#temporals.format(plan.planDt, 'yyyy-MM-dd')}"></td>
|
||||||
<td th:text="${plan.wrtNm}"></td>
|
<td th:text="${plan.wrtNm}"></td>
|
||||||
<td th:text="${#temporals.format(plan.wrtDt, 'yyyy-MM-dd HH:mm')}"></td>
|
<td th:text="${#temporals.format(plan.wrtDt, 'yyyy-MM-dd HH:mm')}"></td>
|
||||||
<td></td>
|
<td th:text="${plan.fileCnt eq null?'파일 없음':#strings.concat(plan.fileCnt,' 건')}"></td>
|
||||||
<td th:if="${plan.sectionApprv eq 'T'}">계장결재</td>
|
<td th:if="${plan.sectionApprv eq 'T'}">계장결재</td>
|
||||||
<td th:if="${plan.sectionApprv eq 'F'}">계장반려</td>
|
<td th:if="${plan.sectionApprv eq 'F'}">계장반려</td>
|
||||||
<td th:if="${plan.headApprv eq 'T'}">부장결재</td>
|
<td th:if="${plan.headApprv eq 'T'}">부장결재</td>
|
||||||
|
|
@ -123,14 +123,14 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div class="modal fade" id="planEditModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="planEditModalLabel" aria-hidden="true">
|
<div class="modal fade" id="planEditModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="planEditModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-xl">
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||||
<div class="modal-content" id="planEditModalContent">
|
<div class="modal-content" id="planEditModalContent">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade" id="planViewModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="planViewModalLabel" aria-hidden="true">
|
<div class="modal fade" id="planViewModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="planViewModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-xl">
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="planViewModalLabel">월간 계획 열람</h5>
|
<h5 class="modal-title" id="planViewModalLabel">월간 계획 열람</h5>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<form method="get" th:action="@{/fpiMgt/monthPlanPage}">
|
||||||
|
<input type="hidden" name="pageIndex" id="pageIndex" th:value="${searchParams.pageIndex}">
|
||||||
|
<div class="row justify-content-between pe-3 py-1">
|
||||||
|
<div class="col-auto">
|
||||||
|
<select class="form-select" name="rowCnt" id="rowCnt">
|
||||||
|
<th:block th:each="num : ${#numbers.sequence(1,5)}">
|
||||||
|
<option th:value="${num*10}" th:text="${num*10}" th:selected="${searchParams.rowCnt eq num*10}"></option>
|
||||||
|
</th:block>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="row justify-content-end">
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="text" class="form-control form-control-sm">
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-sm btn-primary col-auto" id="searchBtn" value="검색">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="row justify-content-start">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>제목</th>
|
||||||
|
<th>시행일자</th>
|
||||||
|
<th>작성자</th>
|
||||||
|
<th>작성일시</th>
|
||||||
|
<th>첨부파일</th>
|
||||||
|
<th>상태</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="planTr" th:each="plan:${planList}">
|
||||||
|
<input type="hidden" class="planKey" th:value="${plan.planKey}">
|
||||||
|
<td><input type="checkbox"></td>
|
||||||
|
<td th:text="${plan.contentTitle}"></td>
|
||||||
|
<td th:text="${#temporals.format(plan.planDt, 'yyyy-MM-dd')}"></td>
|
||||||
|
<td th:text="${plan.wrtNm}"></td>
|
||||||
|
<td th:text="${#temporals.format(plan.wrtDt, 'yyyy-MM-dd HH:mm')}"></td>
|
||||||
|
<td></td>
|
||||||
|
<td th:if="${plan.sectionApprv eq 'T'}">계장결재</td>
|
||||||
|
<td th:if="${plan.sectionApprv eq 'F'}">계장반려</td>
|
||||||
|
<td th:if="${plan.headApprv eq 'T'}">부장결재</td>
|
||||||
|
<td th:if="${plan.headApprv eq 'F'}">부장반려</td>
|
||||||
|
<td th:if="${plan.planState eq 'T'}">임시저장</td>
|
||||||
|
<td th:if="${#strings.isEmpty(plan.sectionApprv) and #strings.isEmpty(plan.headApprv) and plan.planState eq 'S'}">결재대기</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-between">
|
||||||
|
<div class="col-auto"></div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination">
|
||||||
|
<th:block th:if="${searchParams.pageIndex>3}">
|
||||||
|
<li class="page-item" th:data-pageindex="${(searchParams.pageIndex)-3}">
|
||||||
|
<a class="page-link" href="#" aria-label="Previous">
|
||||||
|
<span aria-hidden="true">«</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:each="num : ${#numbers.sequence(searchParams.startNum, searchParams.endNum)}">
|
||||||
|
<li class="page-item" th:data-pageindex="${num}" th:classappend="${searchParams.pageIndex eq num?'active':''}">
|
||||||
|
<a class="page-link" href="#" th:text="${num}"></a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${searchParams.maxNum>searchParams.endNum+2}">
|
||||||
|
<li class="page-item" th:data-pageindex="${(searchParams.pageIndex)+3}">
|
||||||
|
<a class="page-link" href="#" aria-label="Next">
|
||||||
|
<span aria-hidden="true">»</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<input type="button" class="btn btn-success" value="등록" id="addPlanBtn">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</html>
|
||||||
|
|
@ -34,6 +34,9 @@
|
||||||
<div class="mb-3 row justify-content-center">
|
<div class="mb-3 row justify-content-center">
|
||||||
<label for="planDiv" class="col-sm-2 col-form-label text-center">주요 사업계획</label>
|
<label for="planDiv" class="col-sm-2 col-form-label text-center">주요 사업계획</label>
|
||||||
<div class="col-sm-10" id="planDiv">
|
<div class="col-sm-10" id="planDiv">
|
||||||
|
<th:block th:if="${plan.planKey eq null}">
|
||||||
|
<input type='text' class='form-control' name='planInfos'>
|
||||||
|
</th:block>
|
||||||
<th:block th:each="infoList:${plan.mainInfoList}">
|
<th:block th:each="infoList:${plan.mainInfoList}">
|
||||||
<th:block th:if="${infoList.planType eq 'S'}">
|
<th:block th:if="${infoList.planType eq 'S'}">
|
||||||
<input type="text" class="form-control" name="planInfos" th:value="${infoList.planInfo}">
|
<input type="text" class="form-control" name="planInfos" th:value="${infoList.planInfo}">
|
||||||
|
|
@ -47,6 +50,9 @@
|
||||||
<div class="mb-3 row justify-content-center">
|
<div class="mb-3 row justify-content-center">
|
||||||
<label for="detailPlanDiv" class="col-sm-2 col-form-label text-center">사업개요 및 추진계획</label>
|
<label for="detailPlanDiv" class="col-sm-2 col-form-label text-center">사업개요 및 추진계획</label>
|
||||||
<div class="col-sm-10" id="detailPlanDiv">
|
<div class="col-sm-10" id="detailPlanDiv">
|
||||||
|
<th:block th:if="${plan.planKey eq null}">
|
||||||
|
<textarea type='text' name='detailPlanInfos'></textarea>
|
||||||
|
</th:block>
|
||||||
<th:block th:each="infoList:${plan.mainInfoList}">
|
<th:block th:each="infoList:${plan.mainInfoList}">
|
||||||
<th:block th:if="${infoList.planType eq 'D'}">
|
<th:block th:if="${infoList.planType eq 'D'}">
|
||||||
<textarea type='text' name='detailPlanInfos' th:utext="${infoList.planInfo}"></textarea>
|
<textarea type='text' name='detailPlanInfos' th:utext="${infoList.planInfo}"></textarea>
|
||||||
|
|
@ -57,6 +63,23 @@
|
||||||
<button type="button" class="btn btn-sm btn-outline-primary" id="detailPlanAddBtn"><i class="bi bi-plus-lg"></i></button>
|
<button type="button" class="btn btn-sm btn-outline-primary" id="detailPlanAddBtn"><i class="bi bi-plus-lg"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="fileInputer" class="col-sm-2 col-form-label text-center">업로드 자료</label>
|
||||||
|
<div class="col-sm-10" style="min-height: 70px;">
|
||||||
|
<div class="w-100 h-100 border border-info rounded text-center" id="uploadDiv">
|
||||||
|
<th:block th:if="${#arrays.isEmpty(plan.fileList)}">
|
||||||
|
<br>파일을 업로드 해주세요.
|
||||||
|
</th:block>
|
||||||
|
<th:block th:unless="${#arrays.isEmpty(plan.fileList)}">
|
||||||
|
<div class='row-col-6' th:each="planFile:${plan.fileList}">
|
||||||
|
<span th:data-fileseq="${planFile.fileSeq}" th:text="|${planFile.origNm}.${planFile.fileExtn} ${planFile.fileSize}|"></span>
|
||||||
|
<a href='#' class='uploadedFileDelete text-danger text-decoration-none'>삭제</a>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="file" class="d-none" id="fileInputer" multiple>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||||
<input type="hidden" name="planKey" th:value="${plan.planKey}">
|
<input type="hidden" name="planKey" id="viewModalPlanKey" th:value="${plan.planKey}">
|
||||||
<input type="hidden" name="wrtOrgan" th:value="${plan.wrtOrgan}">
|
<input type="hidden" name="wrtOrgan" th:value="${plan.wrtOrgan}">
|
||||||
<input type="hidden" name="planState" id="planState" th:value="${plan.planState}">
|
<input type="hidden" name="planState" id="planState" th:value="${plan.planState}">
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
|
|
@ -38,15 +38,18 @@
|
||||||
</th:block>
|
</th:block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 row">
|
<hr>
|
||||||
<label for="contentTitle" class="col-sm-1 col-form-label text-center">제목</label>
|
<div class="row">
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="mb-3 row">
|
||||||
|
<label for="contentTitle" class="col-sm-2 col-form-label text-center">제목</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="text" class="form-control border-0" id="contentTitle" name="contentTitle" th:value="${plan.contentTitle}" readonly>
|
<input type="text" class="form-control border-0" id="contentTitle" name="contentTitle" th:value="${plan.contentTitle}" readonly>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<label for="planDiv" class="col-sm-1 col-form-label text-center">주요<br>사업계획</label>
|
<label for="planDiv" class="col-sm-2 col-form-label text-center">주요<br>사업계획</label>
|
||||||
<div class="col-sm-10" id="planDiv">
|
<div class="col-sm-10" id="planDiv">
|
||||||
<th:block th:each="infoList:${plan.mainInfoList}">
|
<th:block th:each="infoList:${plan.mainInfoList}">
|
||||||
<th:block th:if="${infoList.planType eq 'S'}">
|
<th:block th:if="${infoList.planType eq 'S'}">
|
||||||
|
|
@ -54,10 +57,10 @@
|
||||||
</th:block>
|
</th:block>
|
||||||
</th:block>
|
</th:block>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<label for="detailPlanDiv" class="col-sm-1 col-form-label text-center">사업개요<br>및<br>추진계획</label>
|
<label for="detailPlanDiv" class="col-sm-2 col-form-label text-center">사업개요<br>및<br>추진계획</label>
|
||||||
<div class="col-sm-10" id="detailPlanDiv">
|
<div class="col-sm-10" id="detailPlanDiv">
|
||||||
<th:block th:each="infoList:${plan.mainInfoList}">
|
<th:block th:each="infoList:${plan.mainInfoList}">
|
||||||
<th:block th:if="${infoList.planType eq 'D'}">
|
<th:block th:if="${infoList.planType eq 'D'}">
|
||||||
|
|
@ -65,4 +68,31 @@
|
||||||
</th:block>
|
</th:block>
|
||||||
</th:block>
|
</th:block>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>파일명</th>
|
||||||
|
<th>사이즈</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<th:block th:if="${#arrays.isEmpty(plan.fileList)}">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">파일이 없습니다.</td>
|
||||||
|
</tr>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:unless="${#arrays.isEmpty(plan.fileList)}">
|
||||||
|
<th:block th:each="file:${plan.fileList}">
|
||||||
|
<tr class="fileInfoTr">
|
||||||
|
<td><a href="#" class="fileDownLink" data-board="monthPlan" th:data-fileseq="${file.fileSeq}" th:text="|${file.origNm}.${file.fileExtn}|"></a></td>
|
||||||
|
<td th:text="${file.fileSize}"></td>
|
||||||
|
</tr>
|
||||||
|
</th:block>
|
||||||
|
</th:block>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Loading…
Reference in New Issue