[Admin_공지사항] enum 공지사항 유형 / 조회수 증가 / 다중 검색 기능(1)
2024. 1. 20. 23:01ㆍProject/ShoppingMall
반응형
🟢 Constant
▪️ 공지사항 유형 설정
public enum NoticeRole {
POINT("적립금"),
DELIVERY("배송공지"),
PRODUCT("상품공지"),
EVENT("이벤트"),
ETC("기타"),
TIP("청소꿀팁");
private final String description;
NoticeRole(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
🟢 Entity
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name = "notice")
@SequenceGenerator(
name = "notice_SEQ",
sequenceName = "notice_SEQ",
initialValue = 1,
allocationSize = 1)
public class NoticeEntity extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "notice_SEQ")
@Column(name="noticeId")
private Integer noticeId;
@Column(name="noticeCategory", nullable = false)
private String noticeCategory; //공지유형
@Column(name="noticeTitle",nullable = false,length = 50)
private String noticeTitle; //제목
@Column(name="noticeWriter", nullable = false,length = 20)
private String noticeWriter; //작성자
@Column(name="noticeContent", nullable = false,length = 2000)
private String noticeContent; //공지내용
@Column(name = "noticeView")
private Integer noticeView;
@Column(name="noticeRole")
@Enumerated(EnumType.STRING)
private NoticeRole noticeRole;
}
🟢 DTO
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class NoticeDTO {
private Integer noticeId;
private NoticeRole noticeRole;
@NotEmpty(message="제목은 생략할 수 없습니다.")
private String noticeTitle; //제목
@NotEmpty(message="작성자는 생략할 수 없습니다.")
private String noticeWriter; //작성자
@NotEmpty(message="공지내용은 생략할 수 없습니다.")
private String noticeContent; //공지내용
private Integer noticeView; //조회수
private LocalDateTime reDate; //생성날짜
private LocalDateTime modDate; //수정날짜
}
🟢 Repository
▪️ 검색조건 : 공지유형, 제목 또는 내용
@Repository
public interface NoticeRepository extends JpaRepository<NoticeEntity, Integer> {
//검색조건
//공지 유형
@Query("SELECT n FROM NoticeEntity n WHERE n.noticeRole = :noticeRole")
Page<NoticeEntity> findByNoticeRole (@Param("noticeRole")NoticeRole noticeRole, Pageable pageable);
//제목 또는 내용
@Query("SELECT n FROM NoticeEntity n WHERE n.noticeTitle LIKE %:keyword% OR n.noticeContent like %:keyword%")
Page<NoticeEntity> findByNoticeTitleOrNoticeContent (@Param("keyword") String keyword, Pageable pageable);
}
🟢 Service
@Service
@RequiredArgsConstructor
@Transactional
public class NoticeService {
private final NoticeRepository noticeRepository;
private final CommentRepository commentRepository;
private final ModelMapper modelMapper = new ModelMapper();
//공지등록
public NoticeEntity insert(NoticeDTO noticeDTO) throws Exception {
NoticeEntity noticeEntity = modelMapper.map(noticeDTO, NoticeEntity.class);
noticeRepository.save(noticeEntity);
return noticeEntity;
}
//공지목록 (관리자)
public Page<NoticeDTO> findAll(String type, String keyword, String noticeRole, Pageable page) throws Exception {
int currentpage = page.getPageNumber()-1;
int blockLimit = 10;
Pageable pageable = PageRequest.of(currentpage, blockLimit, Sort.by(Sort.Direction.DESC, "noticeId"));
Page<NoticeEntity> noticeEntityPage;
if("tc".equals(type) && keyword != null && !keyword.isEmpty()) {
noticeEntityPage = noticeRepository.findByNoticeTitleOrNoticeContent(keyword, pageable);
} else if("r".equals(type) && noticeRole != null && !noticeRole.isEmpty()) {
noticeEntityPage = noticeRepository.findByNoticeRole(NoticeRole.valueOf(noticeRole), pageable);
} else {
noticeEntityPage = noticeRepository.findAll(pageable);
}
List<NoticeDTO> noticeDTOS = noticeEntityPage.stream()
.map(noticeEntity -> modelMapper.map(noticeEntity, NoticeDTO.class))
.collect(Collectors.toList());
return new PageImpl<>(noticeDTOS, pageable, noticeEntityPage.getTotalElements());
}
//공지상세 (관리자)
public NoticeDTO findOne(Integer noticeId) throws Exception {
Optional<NoticeEntity> noticeEntity = noticeRepository.findById(noticeId);
NoticeDTO noticeDTO = modelMapper.map(noticeEntity, NoticeDTO.class);
return noticeDTO;
}
//공지수정
//작성일, 조회수 유지
public void update(NoticeDTO noticeDTO) throws Exception {
Integer noticeId = noticeDTO.getNoticeId();
Optional<NoticeEntity> noticeEntity = noticeRepository.findById(noticeId);
if(noticeEntity.isPresent()) {
NoticeEntity update = noticeEntity.get();
LocalDateTime creationDate = update.getReDate();
Integer noticeView = update.getNoticeView();
modelMapper.map(noticeDTO, update);
update.setReDate(creationDate);
update.setNoticeView(noticeView);
noticeRepository.save(update);
}
}
//공지삭제
//댓글 삭제 후, 글 삭제
public void delete(Integer noticeId) throws Exception {
commentRepository.deleteAll();
noticeRepository.deleteById(noticeId);
}
}
🟢 Controller
@Controller
@Transactional
@RequiredArgsConstructor
public class NoticeController {
private final NoticeService noticeService;
private final CommentService commentService;
//관리자
//공지등록폼
@GetMapping("/admin_noticeinsert")
public String noticeForm(Model model) throws Exception {
NoticeDTO noticeDTO = new NoticeDTO();
model.addAttribute("noticeDTO", noticeDTO);
model.addAttribute("noticeRole", NoticeRole.values());
return "admin/notice_insert";
}
//공지등록처리
@PostMapping("/admin_noticeinsert")
public String noticeProc(@Valid NoticeDTO noticeDTO, BindingResult bindingResult, Model model) throws Exception {
if(bindingResult.hasErrors()) {
model.addAttribute("noticeRole", NoticeRole.values());
return "admin/notice_insert";
} noticeService.insert(noticeDTO);
return "redirect:/admin_noticelist";
}
//공지목록
@GetMapping("/admin_noticelist")
public String noticeList(@RequestParam(value = "type", defaultValue = "") String type,
@RequestParam(value = "keyword", defaultValue = "") String keyword,
@RequestParam(value = "noticeRole", defaultValue = "") String noticeRole,
@PageableDefault(page = 1) Pageable pageable,
Model model) throws Exception {
Page<NoticeDTO> noticeDTOS = noticeService.findAll(type, keyword, noticeRole, pageable);
int blockLimit = 5;
int startPage = (((int)(Math.ceil((double) pageable.getPageNumber() / blockLimit))) - 1) * blockLimit + 1;
int endPage = Math.min(startPage + blockLimit - 1, noticeDTOS.getTotalPages());
int prevPage = noticeDTOS.getNumber();
int currentPage = noticeDTOS.getNumber() + 1;
int nextPage = noticeDTOS.getNumber() + 2;
int lastPage = noticeDTOS.getTotalPages();
model.addAttribute("noticeDTOS", noticeDTOS);
model.addAttribute("startPage", startPage);
model.addAttribute("endPage", endPage);
model.addAttribute("prevPage", prevPage);
model.addAttribute("currentPage", currentPage);
model.addAttribute("nextPage", nextPage);
model.addAttribute("lastPage", lastPage);
model.addAttribute("type", type);
model.addAttribute("keyword", keyword);
return "/admin/notice_list";
}
//공지상세
@GetMapping("/admin_noticeread")
public String detail(Integer noticeId, Model model) throws Exception {
NoticeDTO noticeDTO = noticeService.findOne(noticeId);
model.addAttribute("noticeDTO", noticeDTO);
return "admin/notice_read";
}
//공지수정 폼
@GetMapping("/admin_noticeupdate")
public String noticeDetailForm(Integer noticeId, Model model) throws Exception {
NoticeDTO noticeDTO = noticeService.findOne(noticeId);
model.addAttribute("noticeDTO", noticeDTO);
model.addAttribute("noticeRole", NoticeRole.values());
return "/admin/notice_update";
}
//수정처리
@PostMapping("/admin_noticeupdate")
public String noticeDetailProc(@Valid NoticeDTO noticeDTO,
BindingResult bindingResult,
Model model) throws Exception {
if(bindingResult.hasErrors()){
model.addAttribute("noticeRole", NoticeRole.values());
return "/admin/notice_update";
} noticeService.update(noticeDTO);
return "redirect:/admin_noticelist";
}
//공지삭제
@GetMapping("/admin_noticeremove")
public String noticeDelete(Integer noticeId) throws Exception{
noticeService.delete(noticeId);
return "redirect:/admin_noticelist";
}
}
🟢 insert.html
더보기
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/adminlayout}">
<head>
<meta charset="utf-8">
<title>ADMIN - Notice Insert</title>
</head>
<body>
<div layout:fragment="content">
<div class="main-wrapper wrapper-2">
<div class="breadcrumb-area breadcrumb-padding-8">
<div class="container">
<div class="breadcrumb-content text-center">
<div class="breadcrumb-title">
<h2>Notice</h2>
</div>
</div>
</div>
</div>
<div class="notice-area bg-white pb-130">
<div class="container">
<div class="notice-info-wrap">
<form th:action="@{/admin_noticeinsert}" method="post" th:object="${noticeDTO}">
<input type="hidden" name="noticeView" value="0">
<table class="table notice-table">
<tbody>
<tr>
<th scope="row" class="notice-padding">제목</th>
<td class="notice-info">
<input type="text" name="noticeTitle" id="noticeTitle">
<span class="text-danger" th:if="${#fields.hasErrors('noticeTitle')}" th:errors="*{noticeTitle}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">작성자</th>
<td class="notice-info">
<input type="text" name="noticeWriter" id="noticeWriter">
<span class="text-danger" th:if="${#fields.hasErrors('noticeWriter')}" th:errors="*{noticeWriter}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지유형</th>
<td>
<div class="sidebar-widget update">
<div class="sidebar-archive-wrap">
<select name="noticeRole" id="noticeRole">
<option value="" th:selected="${noticeRole == ''}"> == 선택 == </option>
<option value="DELIVERY" th:selected="${noticeRole=='DELIVERY'}">배송공지</option>
<option value="PRODUCT" th:selected="${noticeRole=='PRODUCT'}">상품공지</option>
<option value="TIP" th:selected="${noticeRole=='TIP'}">청소꿀팁</option>
<option value="EVENT" th:selected="${noticeRole=='EVENT'}">이벤트</option>
<option value="POINT" th:selected="${noticeRole=='POINT'}">적립금</option>
<option value="ETC" th:selected="${noticeRole=='ETC'}">기타</option>
</select>
<span class="text-danger" th:if="${#fields.hasErrors('noticeRole')}" th:errors="*{noticeRole}"></span>
</div>
</div>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지내용</th>
<td class="notice-info">
<textarea rows="15" name="noticeContent" id="noticeContent"></textarea>
<span class="text-danger" th:if="${#fields.hasErrors('noticeContent')}" th:errors="*{noticeContent}"></span>
</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col d-flex justify-content-center">
<div class="notice-button-container">
<div class="notice-content">
<div class="notice-btn-date-wrap list-btn">
<div class="notice-btn">
<button type="submit" class="btn btn-lg">등록</button>
<button type="reset" class="btn btn-lg">다시</button>
<button type="button" class="btn btn-lg" th:onclick="|location.href='@{/admin_noticelist}'|">취소</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
🟢 list.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/adminlayout}">
<head>
<meta charset="utf-8">
<title>ADMIN - Notice List</title>
</head>
<body>
<div layout:fragment="content">
<div class="main-wrapper wrapper-2">
<div class="breadcrumb-area breadcrumb-padding-8">
<div class="container">
<div class="breadcrumb-content text-center">
<div class="breadcrumb-title">
<h2>PRODUCT</h2>
</div>
<ul>
<li>
<a href="/admin_noticelist">공지목록</a>
</li>
<li>
|
</li>
<li>
<a href="/admin_noticeinsert">공지등록</a>
</li>
</ul>
</div>
</div>
</div>
<div class="wishlist-area bg-white pb-130">
<div class="container">
<div class="row justify-content-center">
<div class="col-sm-4"></div>
<div class="col-sm-8">
<!-- 검색 폼 -->
<form th:action="@{/admin_noticelist}" method="get" id="searchForm">
<input type="hidden" name="page" value="1">
<div class="input-group mb-3 mt-5">
<!-- 대분류 셀렉트 리스트 -->
<select class="form-select" name="type" id="searchType">
<option value="" th:selected="${type == ''}">== 선택 ==</option>
<option value="tc" th:selected="${type == 'tc'}">제목+내용</option>
<option value="r" th:selected="${type == 'r'}">공지유형</option>
</select>
<!-- 검색창 -->
<input type="text" class="form-control" name="keyword" id="keyword" th:value="${keyword}" style="display: none;"></input>
<!-- 중분류(공지유형) 셀렉트 리스트 -->
<select class="form-select" name="noticeRole" id="noticeRole" style="display: none;">
<option value="" th:selected="${noticeRole == ''}">== 선택 ==</option>
<option value="DELIVERY" th:selected="${noticeRole == 'DELIVERY'}">배송공지</option>
<option value="PRODUCT" th:selected="${noticeRole == 'LIVING'}">상품공지</option>
<option value="TIP" th:selected="${noticeRole == 'TIP'}">청소꿀팁</option>
<option value="EVENT" th:selected="${noticeRole == 'EVENT'}">이벤트</option>
<option value="POINT" th:selected="${noticeRole == 'POINT'}">적립금</option>
<option value="ETC" th:selected="${noticeRole == 'ETC'}">기타</option>
</select>
<!-- 검색 버튼 -->
<button type="submit" class="btn btn-primary admin-dart" name="searchButton">검색</button>
<!-- 리셋 버튼 -->
<button type="button" class="btn btn-light admin-light " name="searchButton" th:onclick="|location.href='@{/admin_noticelist}'|">다시</button>
</div>
</form>
<!-- JavaScript 코드 -->
<script>
document.getElementById('searchType').addEventListener('change', function () {
resetSearchForm();
});
function resetSearchForm() {
var selectedSearchType = document.getElementById('searchType').value;
var selectedNoticeRole = document.getElementById('noticeRole').value;
applyStylesAfterSearch(selectedSearchType, selectedNoticeRole);
toggleElementDisplay('keyword', selectedSearchType === 'tc');
}
function applyStylesAfterSearch(searchType, noticeRole) {
toggleElementDisplay('noticeRole', searchType === 'r');
// 검색 타입이 제목+내용일 때 keyword 입력 필드를 보이게 설정
toggleElementDisplay('keyword', searchType === 'tc');
if (searchType === 'r') {
var noticeRoleElement = document.getElementById('noticeRole');
if (noticeRoleElement.value === '') {
noticeRoleElement.value = localStorage.getItem('selectedNoticeRole') || noticeRole;
}
}
}
function toggleElementDisplay(elementId, condition) {
var element = document.getElementById(elementId);
element.style.display = condition ? 'block' : 'none';
}
// 페이지 로드 시 초기 스타일 적용
applyStylesAfterSearch(
document.getElementById('searchType').value,
document.getElementById('noticeRole').value
);
// 페이지 로드 시 localStorage에서 이전 선택 값을 가져와 적용
window.onload = function () {
applyStylesAfterSearch(
document.getElementById('searchType').value,
document.getElementById('noticeRole').value,
);
}
// 페이지 언로드 시 localStorage에 현재 선택 값을 저장
window.onbeforeunload = function () {
localStorage.setItem('selectedNoticeRole', document.getElementById('noticeRole').value);
}
</script>
</div>
</div>
</div>
<div class="row mb-20"></div>
<div class="container">
<div class="row">
<div class="col-12">
<form action="#">
<div class="wishlist-table-content">
<span class="table-content">
<table>
<thead>
<tr>
<th class="width-price">번호</th>
<th class="width-price">공지종류</th> <!-- 정책공지, 배송공지, 상품공지, 이벤트, 기타 -->
<th class="width-price">제목</th>
<th class="width-price">작성자</th>
<th class="width-price">작성날짜</th>
</tr>
</thead>
<tbody>
<tr th:each="data:${noticeDTOS}">
<td th:text="${data.noticeId}"></td>
<td th:if="${data != null}">
<div th:if="${data.noticeRole != null}">
<div th:text="${data.noticeRole.description}"></div>
</div>
<div th:unless="${data.noticeRole != null}"></div>
</td>
<td>
<a th:href="@{/admin_noticeread(noticeId=${data.noticeId})}" th:text="${data.noticeTitle}"></a>
</td>
<td th:text="${data.noticeWriter}"></td>
<td>
<span th:text="${#temporals.format(data.reDate, 'yyyy-MM-dd')}"></span>
</td>
</tr>
</tbody>
</table>
<!-- 페이지 번호 추가 -->
<div class="pagination-style text-center mt-30" th:if="${lastPage > 1}">
<ul>
<li th:unless="${startPage == 1}">
<a th:href="@{/admin_noticelist(type=${type}, keyword=${keyword}, page=1)}">처음</a>
</li>
<li th:unless="${currentPage == 1}">
<a th:href="@{/admin_noticelist(type=${type}, keyword=${keyword}, page=${prevPage})}"><</a>
</li>
<li th:each="page: ${#numbers.sequence(startPage, endPage)}">
<a th:if="${currentPage != page}" th:href="@{/admin_noticelist(type=${type}, keyword=${keyword}, page=${page})}">[[${page}]]</a>
<a th:if="${currentPage == page}" href="#" class="active">[[${page}]]</a>
</li>
<li th:unless="${currentPage == lastPage}">
<a th:href="@{/admin_noticelist(type=${type}, keyword=${keyword}, page=${nextPage})}">></a>
</li>
<li th:unless="${endPage == lastPage}">
<a th:href="@{/admin_noticelist(type=${type}, keyword=${keyword}, page=${lastPage})}">끝</a>
</li>
</ul>
</div>
<!-- 페이지 번호 추가 끝 -->
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Main JS -->
<script src="assets/js/main.js"></script>
</div>
</body>
</html>
🟢 detail.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/adminlayout}">
<head>
<meta charset="UTF-8">
<title>Notice</title>
</head>
<body>
<div layout:fragment="content">
<div class="main-wrapper wrapper-2">
<div class="breadcrumb-area breadcrumb-padding-8">
<div class="container">
<div class="breadcrumb-content text-center">
<div class="breadcrumb-title">
<h2>Notice</h2>
</div>
</div>
</div>
</div>
<div class="notice-area bg-white pb-130">
<div class="container">
<div class="notice-info-wrap">
<table class="table notice-table">
<tbody>
<tr>
<input type="hidden" name="noticeId" th:value="${noticeDTO.noticeId}">
<th scope="row" class="notice-padding">제목</th>
<td th:text="${noticeDTO.noticeTitle}" readonly></td>
</tr>
<tr>
<th scope="row" class="notice-padding">작성자</th>
<td th:text="${noticeDTO.noticeWriter}" readonly></td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지유형</th>
<td th:text="${noticeDTO.noticeRole.description}" readonly></td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지내용</th>
<td class="notice-info">
<textarea rows="15" th:text="${noticeDTO.noticeContent}" name="noticeContent" readonly></textarea>
</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col d-flex justify-content-center">
<div class="notice-button-container">
<div class="notice-content">
<div class="notice-btn-date-wrap list-btn">
<div class="notice-btn">
<button type="button" class="btn btn-lg"
th:onclick="|location.href='@{/admin_noticeupdate(noticeId=${noticeDTO.noticeId})}'|">수정</button>
<button type="button" class="btn btn-lg"
th:onclick="|location.href='@{/admin_noticelist}'|">목록</button>
<button type="button" class="btn btn-lg"
th:onclick="|location.href='@{/admin_noticeremove(noticeId=${noticeDTO.noticeId})}'|">삭제</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Main JS -->
<script src="assets/js/main.js"></script>
</div>
</body>
</html>
🟢 update.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/adminlayout}">
<head>
<meta charset="utf-8">
<title>ADMIN - Notice Update</title>
</head>
<body>
<div layout:fragment="content">
<div class="main-wrapper wrapper-2">
<div class="breadcrumb-area breadcrumb-padding-8">
<div class="container">
<div class="breadcrumb-content text-center">
<div class="breadcrumb-title">
<h2>Notice</h2>
</div>
</div>
</div>
</div>
<div class="notice-area bg-white pb-130">
<div class="container">
<div class="notice-info-wrap">
<form th:action="@{/admin_noticeupdate}" method="post" th:object="${noticeDTO}">
<input type="hidden" name="noticeId" th:value="*{noticeId}">
<input type="hidden" name="reDate" th:value="*{reDate}">
<input type="hidden" name="modDate" th:value="*{modDate}">
<table class="table notice-table">
<tbody>
<tr>
<th scope="row" class="notice-padding">제목</th>
<td class="notice-info">
<input type="text" id="noticeTitle" name="noticeTitle" th:field="*{noticeTitle}">
<span class="text-danger" th:if="${#fields.hasErrors('noticeTitle')}" th:errors="*{noticeTitle}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">작성자</th>
<td class="notice-info">
<input type="text" id="noticeWriter" name="noticeWriter" th:field="*{noticeWriter}" >
<span class="text-danger" th:if="${#fields.hasErrors('noticeWriter')}" th:errors="*{noticeWriter}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지유형</th>
<td>
<div class="sidebar-widget update">
<div class="sidebar-archive-wrap">
<select id="noticeRole" name="noticeRole">
<option th:each="state : ${noticeRole}"
th:value="${state.name()}"
th:text="${state.getDescription()}"
th:selected="${state.name() eq noticeDTO.noticeRole?.name()}">
</option>
</select>
</div>
</div>
<span class="text-danger" th:if="${#fields.hasErrors('noticeRole')}" th:errors="*{noticeRole}"></span>
</td>
</tr>
<tr>
<th scope="row" class="notice-padding">공지내용</th>
<td class="notice-info">
<textarea rows="15" th:field="*{noticeContent}" name="noticeContent">
공지내용
</textarea>
<span class="text-danger" th:if="${#fields.hasErrors('noticeContent')}" th:errors="*{noticeContent}"></span>
</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col d-flex justify-content-center">
<div class="notice-button-container">
<div class="notice-content">
<div class="notice-btn-date-wrap list-btn">
<div class="notice-btn">
<button type="submit" class="btn btn-lg">수정</button>
<button type="reset" class="btn btn-lg">다시</button>
<button type="button" class="btn btn-lg" th:onclick="|location.href='@{/admin_noticelist}'|">취소</button>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
반응형
'Project > ShoppingMall' 카테고리의 다른 글
[User_공지사항] 댓글 등록, 목록, 수정 / 댓글 좋아요 싫어요 (3) (0) | 2024.01.25 |
---|---|
[User_공지사항] 게시글 목록 / 상세보기 / 조회수 / 이전 다음 페이지 이동 (2) (0) | 2024.01.23 |
[User_상품진열] enum 카테고리별로 진열 / 상품 상세페이지 / 할인율 계산 / 자동합산 / 연관상품 (0) | 2024.01.20 |
[Admin_상품등록] 다중 이미지 삽입 / 이미지-상품 테이블 조인 / enum 카테고리, 판매 상태(2) (0) | 2024.01.19 |
[Admin_상품등록] 다중 이미지 삽입 / 이미지-상품 테이블 조인 / enum 카테고리, 판매 상태(1) (0) | 2024.01.19 |