게시물 내용 조회, 다운로드 기능 추가.
parent
6a1e3dd031
commit
8457e3343d
|
|
@ -2,16 +2,30 @@ package com.dbnt.kcgfilemanager.controller;
|
|||
|
||||
import com.dbnt.kcgfilemanager.config.LogStatus;
|
||||
import com.dbnt.kcgfilemanager.model.Board;
|
||||
import com.dbnt.kcgfilemanager.model.FileInfo;
|
||||
import com.dbnt.kcgfilemanager.model.UserInfo;
|
||||
import com.dbnt.kcgfilemanager.service.BoardService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.Principal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
|
|
@ -50,14 +64,147 @@ public class BoardController {
|
|||
@GetMapping("/selectBoardContent")
|
||||
public ModelAndView selectBoardContent(Board content){
|
||||
ModelAndView mav = new ModelAndView("board/contentDetail");
|
||||
mav.addObject("content", boardService.selectContentDetail(content.getContentSeq()));
|
||||
Board target = boardService.selectContentByContentSeqAndViewCntUp(content.getContentSeq());
|
||||
mav.addObject("content", boardService.SelectContentForeignAttribute(target));
|
||||
return mav;
|
||||
}
|
||||
@GetMapping("/selectBoardLog")
|
||||
public ModelAndView selectBoardLog(Board content){
|
||||
ModelAndView mav = new ModelAndView("board/contentLog");
|
||||
mav.addObject("contentSeq", content.getContentSeq());
|
||||
mav.addObject("statusMap", LogStatus.getStatusMap());
|
||||
mav.addObject("logList", boardService.selectContentLog(content.getContentSeq()));
|
||||
return mav;
|
||||
}
|
||||
|
||||
@GetMapping("/fileDownload")
|
||||
public void fileDownload(Integer contentSeq, Integer fileSeq, HttpServletRequest request, HttpServletResponse response){
|
||||
FileInfo downlodFileInfo = boardService.selectDownloadFileInfo(contentSeq, fileSeq);
|
||||
|
||||
BufferedInputStream in;
|
||||
BufferedOutputStream out;
|
||||
try {
|
||||
File file = new File(downlodFileInfo.getSavePath(), downlodFileInfo.getConversionName());
|
||||
setDisposition(downlodFileInfo.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();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/fileDownloadToZip")
|
||||
public void fileDownloadToZip(Integer contentSeq, @RequestParam(value = "fileSeq") List<Integer> fileSeqList, HttpServletResponse response){
|
||||
List<FileInfo> targetList = boardService.selectDownloadFileInfoList(contentSeq, fileSeqList);
|
||||
|
||||
BufferedInputStream in;
|
||||
BufferedOutputStream out;
|
||||
|
||||
String tempZipPath = "D:\\kcgFileManager\\tempZip\\";
|
||||
File tempFolder = new File(tempZipPath);
|
||||
if (!tempFolder.exists() || tempFolder.isFile()) {
|
||||
tempFolder.mkdirs();
|
||||
}
|
||||
String downloadFileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
|
||||
|
||||
tempZipPath += downloadFileName+".zip";
|
||||
try{
|
||||
FileOutputStream fout = new FileOutputStream(tempZipPath);
|
||||
ZipOutputStream zout = new ZipOutputStream(fout);
|
||||
for(FileInfo downLoadFile: targetList){
|
||||
ZipEntry zipEntry = new ZipEntry(downLoadFile.getFullName());
|
||||
zout.putNextEntry(zipEntry);
|
||||
|
||||
FileInputStream fin = new FileInputStream(downLoadFile.getSavePath()+"\\"+downLoadFile.getConversionName());
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while((length = fin.read(buffer)) > 0){
|
||||
zout.write(buffer, 0, length);
|
||||
}
|
||||
|
||||
zout.closeEntry();
|
||||
fin.close();
|
||||
}
|
||||
zout.close();
|
||||
|
||||
response.setContentType("application/zip");
|
||||
response.addHeader("Content-Disposition", "attachment; filename="+downloadFileName+".zip");
|
||||
|
||||
FileInputStream fis=new FileInputStream(tempZipPath);
|
||||
ServletOutputStream so=response.getOutputStream();
|
||||
in=new BufferedInputStream(fis);
|
||||
out=new BufferedOutputStream(so);
|
||||
|
||||
byte[] data=new byte[2048];
|
||||
int input;
|
||||
while((input=in.read(data))!=-1){
|
||||
out.write(data,0,input);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
if(out!=null) out.close();
|
||||
if(fis!=null) fis.close();
|
||||
if(in!=null) in.close();
|
||||
if(so!=null) so.close();
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
File tempZip = new File(tempZipPath);
|
||||
tempZip.delete();
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public class BaseModel {
|
|||
@Transient
|
||||
private Integer firstIndex=0; // 쿼리의 시작 row
|
||||
@Transient
|
||||
private Integer viewCnt=10; //한 페이지에 표현되는 row 수
|
||||
private Integer rowCnt=10; //한 페이지에 표현되는 row 수
|
||||
@Transient
|
||||
private Integer startNum=1; // pagination 시작값
|
||||
@Transient
|
||||
|
|
@ -30,13 +30,13 @@ public class BaseModel {
|
|||
private String endDate;
|
||||
|
||||
public void setQueryInfo(){
|
||||
setFirstIndex((getPageIndex()-1)*getViewCnt());
|
||||
setFirstIndex((getPageIndex()-1)*getRowCnt());
|
||||
}
|
||||
|
||||
public void setPaginationInfo(){
|
||||
int contentCnt = getContentCnt();
|
||||
int viewCnt = getViewCnt();
|
||||
int maxNum = (int)Math.ceil(((double)contentCnt)/viewCnt);
|
||||
int rowCnt = getRowCnt();
|
||||
int maxNum = (int)Math.ceil(((double)contentCnt)/rowCnt);
|
||||
setMaxNum(maxNum);
|
||||
|
||||
int pageIndex = getPageIndex();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ public class Board extends BaseModel{
|
|||
private String description;
|
||||
@Column(name = "STATUS")
|
||||
private String status;
|
||||
@Column(name = "VIEW_CNT")
|
||||
private Integer viewCnt;
|
||||
@Column(name = "CREATE_ID")
|
||||
private String createId;
|
||||
@Column(name = "CREATE_DATE")
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ public class FileInfo {
|
|||
private String conversionName;
|
||||
@Column(name = "EXTENTION")
|
||||
private String extention;
|
||||
@Column(name = "FILE_SIZE")
|
||||
private String fileSize;
|
||||
@Column(name = "SAVE_PATH")
|
||||
private String savePath;
|
||||
|
||||
|
|
@ -41,4 +43,8 @@ public class FileInfo {
|
|||
private Integer contentSeq;
|
||||
private Integer fileSeq;
|
||||
}
|
||||
|
||||
public String getFullName(){
|
||||
return originalName+"."+extention;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
|
@ -100,6 +101,7 @@ public class BoardService {
|
|||
fileInfo.setOriginalName(originalFilename[0]);
|
||||
fileInfo.setExtention(originalFilename[1]);
|
||||
fileInfo.setConversionName(saveName);
|
||||
fileInfo.setFileSize(calculationFileSize(file.getSize()));
|
||||
fileInfo.setSavePath(path);
|
||||
fileInfoRepository.save(fileInfo);
|
||||
saveBoardLog(content.getContentSeq(), LogStatus.FILE_ADD, file.getOriginalFilename(), content.getCreateId());
|
||||
|
|
@ -114,14 +116,6 @@ public class BoardService {
|
|||
return getPageTitle(category.getParentSeq())+" > "+category.getCategoryName();
|
||||
}
|
||||
|
||||
public String makeFilePath(Integer categorySeq){
|
||||
BoardCategory category = boardCategoryRepository.findById(categorySeq).orElse(null);
|
||||
if(category.getParentSeq()==null){
|
||||
return "D:\\kcgFileManager\\"+category.getCategoryName();
|
||||
}
|
||||
return makeFilePath(category.getParentSeq())+"\\"+category.getCategoryName();
|
||||
}
|
||||
|
||||
public List<Board> selectContentList(Board board) {
|
||||
return boardMapper.selectContentList(board);
|
||||
}
|
||||
|
|
@ -129,13 +123,48 @@ public class BoardService {
|
|||
return boardMapper.selectContentListCnt(board);
|
||||
}
|
||||
|
||||
public Board selectContentDetail(Integer contentSeq) {
|
||||
@Transactional
|
||||
public Board selectContentByContentSeqAndViewCntUp(Integer contentSeq) {
|
||||
Board target = boardRepository.findById(contentSeq).orElse(null);
|
||||
target.setHashTagList(boardMapper.selectHashTagListFromContentSeq(contentSeq));
|
||||
target.setChildFileList(fileInfoRepository.findByContentSeqOrderByFileSeqAsc(contentSeq));
|
||||
target.setViewCnt(target.getViewCnt()+1);
|
||||
return target;
|
||||
}
|
||||
|
||||
public List<FileInfo> selectDownloadFileInfoList(Integer contentSeq, List<Integer> fileSeqList) {
|
||||
List<FileInfo> fileList = fileInfoRepository.findByContentSeqOrderByFileSeqAsc(contentSeq);
|
||||
List<FileInfo> targetList = new ArrayList<>();
|
||||
for(FileInfo file: fileList){
|
||||
if(fileSeqList.contains(file.getFileSeq())){
|
||||
targetList.add(file);
|
||||
}
|
||||
}
|
||||
return targetList;
|
||||
}
|
||||
public FileInfo selectDownloadFileInfo(Integer contestSeq, Integer fileSeq){
|
||||
return fileInfoRepository.findById(new FileInfo.FileInfoId(contestSeq, fileSeq)).orElse(null);
|
||||
}
|
||||
|
||||
public List<BoardLog> selectContentLog(Integer contentSeq){
|
||||
return boardMapper.selectBoardLogFromContentSeq(contentSeq);
|
||||
}
|
||||
|
||||
public Board SelectContentForeignAttribute(Board target) {
|
||||
target.setHashTagList(boardMapper.selectHashTagListFromContentSeq(target.getContentSeq()));
|
||||
target.setChildFileList(fileInfoRepository.findByContentSeqOrderByFileSeqAsc(target.getContentSeq()));
|
||||
return target;
|
||||
}
|
||||
|
||||
private String makeFilePath(Integer categorySeq){
|
||||
BoardCategory category = boardCategoryRepository.findById(categorySeq).orElse(null);
|
||||
if(category.getParentSeq()==null){
|
||||
return "D:\\kcgFileManager\\"+category.getCategoryName();
|
||||
}
|
||||
return makeFilePath(category.getParentSeq())+"\\"+category.getCategoryName();
|
||||
}
|
||||
private String calculationFileSize(Long fileSize){
|
||||
String[] units = {"bytes", "KB", "MB"};
|
||||
double unitSelector = Math.floor(Math.log(fileSize)/Math.log(1024));
|
||||
return Math.round((fileSize/Math.pow(1024, unitSelector))*100)/100d+" "+units[(int)unitSelector];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
A.TITLE AS title,
|
||||
C.FILE_CNT AS fileCnt,
|
||||
B.NAME AS createName,
|
||||
A.CREATE_DATE AS createDate
|
||||
A.CREATE_DATE AS createDate,
|
||||
A.VIEW_CNT AS viewCnt
|
||||
FROM BOARD A
|
||||
INNER JOIN USER_INFO B
|
||||
ON A.CREATE_ID = B.USER_ID
|
||||
|
|
@ -19,7 +20,7 @@
|
|||
ON A.CONTENT_SEQ = C.CONTENT_SEQ
|
||||
WHERE A.CATEGORY_SEQ = #{categorySeq}
|
||||
ORDER BY A.CREATE_DATE DESC
|
||||
LIMIT #{viewCnt} OFFSET #{firstIndex}
|
||||
LIMIT #{rowCnt} OFFSET #{firstIndex}
|
||||
</select>
|
||||
<!--<if test="endDate != null and endDate != ''">
|
||||
AND A.CREATE_DATE <= #{endDate}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
</if>
|
||||
</where>
|
||||
ORDER BY CREATE_DATE DESC
|
||||
LIMIT #{viewCnt} OFFSET #{firstIndex}
|
||||
LIMIT #{rowCnt} OFFSET #{firstIndex}
|
||||
</select>
|
||||
|
||||
<select id="selectUserInfoListCnt" resultType="int" parameterType="UserInfo">
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ $(document).on('click', '.contentTr', function (){
|
|||
}
|
||||
})
|
||||
|
||||
$(document).on('change', '#fileCheckAll', function (){
|
||||
$(".fileCheckBox").prop("checked", this.checked);
|
||||
})
|
||||
|
||||
$(document).on('click', '#contentTab', function (){
|
||||
getBoardContent(getContentSeq())
|
||||
})
|
||||
|
|
@ -26,39 +30,67 @@ $(document).on('click', '#logTab', function (){
|
|||
getBoardLog(getContentSeq())
|
||||
})
|
||||
|
||||
$(document).on('click', '.fileDownLink', function (){
|
||||
let url = "/board/fileDownload?"
|
||||
url += "contentSeq="+Number($("#detailViewContentSeq").val());
|
||||
url += "&fileSeq="+$(this).attr("data-fileseq");
|
||||
window.open(encodeURI(url));
|
||||
})
|
||||
$(document).on('click', '#zipDownBtn', function (){
|
||||
const checkFiles = $(".fileCheckBox:checked");
|
||||
if(checkFiles.length>0){
|
||||
let url = "/board/fileDownloadToZip?"
|
||||
url += "contentSeq="+Number($("#detailViewContentSeq").val());
|
||||
checkFiles.each(function (idx, el){
|
||||
url += "&fileSeq="+Number($(el).attr("data-fileseq"))
|
||||
});
|
||||
window.open(encodeURI(url));
|
||||
}else{
|
||||
alert("선택된 파일이 없습니다.")
|
||||
}
|
||||
})
|
||||
|
||||
function getContentSeq(){
|
||||
return $(".contentCheckBox:checked").val();
|
||||
}
|
||||
|
||||
function getBoardContent(contentSeq){
|
||||
if(contentSeq !== undefined){
|
||||
$.ajax({
|
||||
url: '/board/selectBoardContent',
|
||||
data: {contentSeq: contentSeq},
|
||||
type: 'GET',
|
||||
dataType:"html",
|
||||
success: function(html){
|
||||
$("#boardDiv").empty().append(html)
|
||||
},
|
||||
error:function(){
|
||||
const viewContentSeq = $("#detailViewContentSeq").val();
|
||||
if(contentSeq !== viewContentSeq){
|
||||
$.ajax({
|
||||
url: '/board/selectBoardContent',
|
||||
data: {contentSeq: contentSeq},
|
||||
type: 'GET',
|
||||
dataType:"html",
|
||||
success: function(html){
|
||||
$("#contentDiv").empty().append(html)
|
||||
const viewCntTd = $(".contentCheckBox:checked").parents("tr").find(".viewCntTd");
|
||||
viewCntTd.text(Number(viewCntTd.text())+1);
|
||||
},
|
||||
error:function(){
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function getBoardLog(contentSeq){
|
||||
if(contentSeq !== undefined){
|
||||
$.ajax({
|
||||
url: '/board/selectBoardLog',
|
||||
data: {contentSeq: contentSeq},
|
||||
type: 'GET',
|
||||
dataType:"html",
|
||||
success: function(html){
|
||||
$("#boardDiv").empty().append(html)
|
||||
},
|
||||
error:function(){
|
||||
const viewContentSeq = $("#logViewContentSeq").val();
|
||||
if(contentSeq !== viewContentSeq){
|
||||
$.ajax({
|
||||
url: '/board/selectBoardLog',
|
||||
data: {contentSeq: contentSeq},
|
||||
type: 'GET',
|
||||
dataType:"html",
|
||||
success: function(html){
|
||||
$("#logDiv").empty().append(html)
|
||||
},
|
||||
error:function(){
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,12 +24,12 @@
|
|||
<div class="row justify-content-between">
|
||||
<div class="col-auto row">
|
||||
<div class="col-auto">
|
||||
<label for="viewCnt" class="col-form-label">데이터 수</label>
|
||||
<label for="rowCnt" class="col-form-label">데이터 수</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select class="form-select" name="viewCnt" id="viewCnt">
|
||||
<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.viewCnt==num*10}"></option>
|
||||
<option th:value="${num*10}" th:text="${num*10}" th:selected="${searchParams.rowCnt==num*10}"></option>
|
||||
</th:block>
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||
contentDetail
|
||||
<div class="p-3">
|
||||
<input type="hidden" id="detailViewContentSeq" th:value="${content.contentSeq}">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto"><h3 th:text="${content.title}"></h3></div>
|
||||
<div class="col-auto" th:text="|조회수: ${content.viewCnt}|"></div>
|
||||
</div>
|
||||
<div class="row justify-content-between border-bottom pb-3">
|
||||
<div class="col-auto" th:text="${#temporals.format(content.createDate, 'yyyy-MM-dd HH:mm:ss')}"></div>
|
||||
<div class="col-auto" th:text="${content.createId}"></div>
|
||||
</div>
|
||||
<div class="row border-bottom py-3">
|
||||
<div class="col-8">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="fileCheckAll"></th>
|
||||
<th>파일명</th>
|
||||
<th>사이즈</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<th:block th:each="fileInfo:${content.childFileList}">
|
||||
<tr class="fileInfoTr">
|
||||
<td><input type="checkbox" class="fileCheckBox" th:data-fileseq="${fileInfo.fileSeq}"></td>
|
||||
<td><a href="#" class="fileDownLink" th:data-fileseq="${fileInfo.fileSeq}" th:text="|${fileInfo.originalName}.${fileInfo.extention}|"></a></td>
|
||||
<td th:text="${fileInfo.fileSize}"></td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-4 my-auto">
|
||||
<button type="button" class="btn btn-info m-1" id="zipDownBtn"><i class="bi bi-file-zip"></i> 선택된 파일<br>압축 다운로드</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row border-bottom py-3">
|
||||
<div class="col-12" th:utext="${content.description}"></div>
|
||||
</div>
|
||||
<div class="row py-3">
|
||||
<th:block th:each="hashTag:${content.hashTagList}">
|
||||
<div class="col-auto" th:text="'#'+${hashTag.tagName}"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
<main class="pt-3">
|
||||
<h4 th:text="${pageTitle}"></h4>
|
||||
<div class="row mx-0">
|
||||
<div class="col-12 card text-center">
|
||||
<div class="col-12 card">
|
||||
<div class="card-body">
|
||||
<div class="row justify-content-start">
|
||||
<div class="col-7">
|
||||
|
|
@ -21,12 +21,12 @@
|
|||
<div class="row justify-content-between">
|
||||
<div class="col-auto row">
|
||||
<div class="col-auto">
|
||||
<label for="viewCnt" class="col-form-label">데이터 수</label>
|
||||
<label for="rowCnt" class="col-form-label">데이터 수</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select class="form-select" name="viewCnt" id="viewCnt">
|
||||
<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.viewCnt==num*10}"></option>
|
||||
<option th:value="${num*10}" th:text="${num*10}" th:selected="${searchParams.rowCnt==num*10}"></option>
|
||||
</th:block>
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
</div>
|
||||
</form>
|
||||
<div class="row-cols-6">
|
||||
<table class="table table-striped">
|
||||
<table class="table table-striped text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
|
|
@ -77,6 +77,7 @@
|
|||
<th>파일 수</th>
|
||||
<th>작성자</th>
|
||||
<th>작성일</th>
|
||||
<th>조회수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -93,6 +94,7 @@
|
|||
<th:block th:if="${#dates.format(#dates.createNow(), 'yyyy-MM-dd')} != ${#temporals.format(content.createDate, 'yyyy-MM-dd')}">
|
||||
<td th:text="${#temporals.format(content.createDate, 'yyyy-MM-dd')}"></td>
|
||||
</th:block>
|
||||
<td class="viewCntTd" th:text="${content.viewCnt}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -128,15 +130,22 @@
|
|||
<div class="col-5">
|
||||
<ul class="nav nav-tabs" id="boardTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="contentTab" data-bs-toggle="tab" type="button" role="tab">내용</button>
|
||||
<button class="nav-link active" id="contentTab" data-bs-toggle="tab" data-bs-target="#contentDiv" type="button" role="tab">내용</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="logTab" data-bs-toggle="tab" type="button" role="tab">이력</button>
|
||||
<button class="nav-link" id="logTab" data-bs-toggle="tab" data-bs-target="#logDiv" type="button" role="tab">이력</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content border border-top-0" id="boardDiv">
|
||||
<div class="py-5">
|
||||
<h3>왼쪽 목록에서 선택해주세요.</h3>
|
||||
<div class="tab-pane fade show active" id="contentDiv" role="tabpanel" aria-labelledby="contentTab">
|
||||
<div class="py-5 text-center">
|
||||
<h3>왼쪽 목록에서 선택해주세요.</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="logDiv" role="tabpanel" aria-labelledby="logTab">
|
||||
<div class="py-5 text-center">
|
||||
<h3>왼쪽 목록에서 선택해주세요.</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||
<div class="p-3">
|
||||
<input type="hidden" id="logViewContentSeq" th:value="${contentSeq}">
|
||||
<div class="row-cols-6 overflow-auto">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
|
|
|
|||
Loading…
Reference in New Issue