[Admin_상품등록] 다중 이미지 삽입 / 이미지-상품 테이블 조인 / enum 카테고리, 판매 상태(2)

2024. 1. 19. 22:53Project/ShoppingMall

반응형

 

[Admin_상품등록] 다중 이미지 삽입 / 이미지-상품 테이블 조인 / enum 카테고리, 판매 상태(1)

🟢 Constant 열거형 : 고정된 값들의 집합 ▪️ CategoryTypeRole 제품 카테고리 설정 CategoryTypeRole이라는 열거형 정의 열거 상수(ALL, LIVING, BATHROOM, KITCHEN, MEMBERSALE)는 해당 카테고리의 설명(description)을

dalhyehye.tistory.com

 

 

 

 

 

🟢 상품 등록

더보기
<!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 - Product Insert</title>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
</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>
                </div>
            </div>
        </div>

        <div class="notice-area bg-white pb-130">
            <div class="container">
                <div class="notice-info-wrap">
                    <form th:action="@{'/admin_product_insert'}" method="post" enctype="multipart/form-data" th:object="${productDTO}">
                        <table class="table notice-table">
                            <tbody>
                            <tr>
                                <th scope="row" class="notice-padding" id="productName">상품명</th>
                                <td class="notice-info">
                                    <input type="text" name="productName" th:field="*{productName}">
                                    <p class="text-danger" th:if="${#fields.hasErrors('productName')}" th:errors="*{productName}"></p>
                                </td>
                            </tr>
                            <tr>
                                <th scope="row" class="notice-padding" id="productContent">상품설명</th>
                                <td class="notice-info">
                                    <input type="text" name="productContent" th:field="*{productContent}">
                                    <p class="text-danger" th:if="${#fields.hasErrors('productContent')}" th:errors="*{productContent}"></p>
                                </td>
                            </tr>

                            <tr>
                                <th scope="row" class="notice-padding" id="CategoryTypeRole">카테고리</th>
                                <td class="notice-info">
                                    <div class="sidebar-widget update">
                                        <div class="sidebar-archive-wrap">
                                            <select name="CategoryTypeRole" th:field="*{categoryTypeRole}">
                                                <option th:each="state : ${categoryType}"
                                                        th:unless="${state.name() == 'ALL'}"
                                                        th:value="${state.name()}"
                                                        th:text="${state.getDescription()}"
                                                        th:selected="${state.name() eq data?.categoryTypeRole?.name()}"></option>
                                                </option>
                                            </select>
                                        </div>
                                    </div>
                                </td>
                            </tr>

                            <!-- 상품 등록 폼 -->
                            <tr>
                                <th scope="row" class="notice-padding">소비자가</th>
                                <td class="notice-info">
                                    <input type="text" id="productCost" name="productCost" th:field="*{productCost}" oninput="calculateDiscount()">
                                    <p class="text-danger" th:if="${#fields.hasErrors('productCost')}" th:errors="*{productCost}"></p>
                                </td>
                            </tr>

                            <!-- 판매가 -->
                            <tr>
                                <th scope="row" class="notice-padding" id="productPrice">판매가</th>
                                <td class="notice-info">
                                    <input type="text" id="productPrice" name="productPrice" th:field="*{productPrice}" oninput="calculateDiscount()">
                                    <p class="text-danger" th:if="${#fields.hasErrors('productPrice')}" th:errors="*{productPrice}"></p>
                                </td>
                            </tr>

                            <tr>
                                <th scope="row" class="notice-padding" id="productCnt">재고수</th>
                                <td class="notice-info">
                                    <input type="number" name="productCnt" th:field="*{productCnt}">
                                    <p class="text-danger" th:if="${#fields.hasErrors('productCnt')}" th:errors="*{productCnt}"></p>
                                </td>
                            </tr>
                            <tr>
                                <th scope="row" class="notice-padding" id="SellStateRole">판매상태</th>
                                <td class="notice-info">
                                    <div class="sidebar-widget update">
                                        <div class="sidebar-archive-wrap">
                                            <select name="SellStateRole">
                                                <option th:each="state:${sellsState}"
                                                        th:value="${state.name()}"
                                                        th:text="${state.getDescription()}"
                                                        th:selected="${state.name() eq data?.sellStateRole?.name()}"></option>
                                                </option>
                                            </select>
                                        </div>
                                    </div>
                                </td>
                            </tr>

                            <tr>
                                <th scope="row" class="notice-padding" id="productDetail">상품정보</th>
                                <td class="notice-info">
                                    <textarea rows="7" name="productDetail" th:field="*{productDetail}"></textarea>
                                </td>
                            </tr>

                            <!-- 대표 이미지 -->
                            <tr>
                                <th scope="row" class="notice-padding" id="imageDTOs[0]">대표이미지</th>
                                <td class="notice-info">
                                    <input type="hidden" name="imageDTOs[0].imageType" value="0">
                                    <input type="file" name="images" onchange="previewImage(this, 'previewImg0')">
                                    <img id="previewImg0" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
                                </td>
                            </tr>
                            <!-- 서브 이미지 -->
                            <tr>
                                <th scope="row" class="notice-padding" id="imageDTOs[1]">서브이미지</th>
                                <td class="notice-info">
                                    <input type="hidden" name="imageDTOs[1].imageType" value="1">
                                    <input type="file" name="images" onchange="previewImage(this, 'previewImg1')">
                                    <img id="previewImg1" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
                                </td>
                            </tr>
                            <!-- 상세 이미지 -->
                            <tr>
                                <th scope="row" class="notice-padding" id="imageDTOs[2]">상세이미지</th>
                                <td class="notice-info">
                                    <input type="hidden" name="imageDTOs[2].imageType" value="2">
                                    <input type="file" name="images" onchange="previewImage(this, 'previewImg2')">
                                    <img id="previewImg2" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
                                </td>
                            </tr>
                            <script>
                                // 파일 입력(change) 이벤트를 처리하는 함수
                                function previewImage(input, previewId) {
                                    var preview = document.getElementById(previewId);
                                    var file = input.files[0];
                                    var reader = new FileReader();

                                    reader.onload = function (e) {
                                        preview.src = e.target.result;
                                    };

                                    if (file) {
                                        reader.readAsDataURL(file);
                                    }
                                }

                                // 파일 입력에 이벤트 리스너를 추가합니다.
                                $(document).ready(function () {
                                    $('input[name^="images"]').change(function () {
                                        var index = $(this).attr('id').replace('fileInput', '');
                                        previewImage(this, 'previewImg' + index);
                                    });
                                });
                            </script>
                            </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_productlist}'|">취소</button>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>

                    </form>
                </div>
            </div>
        </div>
    </div>
</div><!-- content 끝-->
</body>
</html>

 

▪️ 사진 등록

enctype="multipart/form-data"

 

 

▪️ enum

scope : 해당 헤더 셀이 괸련되는 셀의 종류를 명시

             (일반 웹 브라우저에서는 아무런 시각적 효과가 나타나지 않지만, 스크린 리더기와 같은 장치에서는 유용하게 사용 됨)

속성값 설명
col 해당 셀이 열(column)을 위한 헤더 셀임을 명시
row 해당 셀이 행(row)을 위한 헤더 셀임을 명시
colgroup 해당 셀이 열의 그룹을 위한 헤더 셀임을 명시
rowgroup 해당 셀이 행의 그룹을 위한 헤더 셀임을 명시

 

                            <tr>
                                <th scope="row" class="notice-padding" id="CategoryTypeRole">카테고리</th>
                                <td class="notice-info">
                                    <div class="sidebar-widget update">
                                        <div class="sidebar-archive-wrap">
                                            <select name="CategoryTypeRole" th:field="*{categoryTypeRole}">
                                                <option th:each="state : ${categoryType}"
                                                        th:unless="${state.name() == 'ALL'}"
                                                        th:value="${state.name()}"
                                                        th:text="${state.getDescription()}"></option>
                                                </option>
                                            </select>
                                        </div>
                                    </div>
                                </td>
                            </tr>
더보기

th:field="*{categoryTypeRole}" → <select> 요소가 categoryTypeRole 필드와 바인딩

 

th:unless="${state.name() == 'ALL'}"   All은 제외( 'ALL'이 아닌 경우에만 해당 )

 

th:text="${state.getDescription()}"    텍스트로 state의 설명을 사용

 

 

▪️ 이미지 삽입

 

> 이미지 관리 DTO → id
private List<ImageDTO> imageDTOs;

> 이미지파일 처리
private List<MultipartFile> images;

 

이미지 파일이름
private String imageFile;

이미지 종류 (대표이미지=0, 서브이미지=1, 상세이미지=2)
private Integer imageType;

                            <tr>
                                <th scope="row" class="notice-padding" id="imageDTOs[0]">대표이미지</th>
                                <td class="notice-info">
                                    <input type="hidden" name="imageDTOs[0].imageType" value="0">
                                    <input type="file" name="images" onchange="previewImage(this, 'previewImg0')">
                                    <img id="previewImg0" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
                                </td>
                            </tr>
                            <script>
                                //매개변수 : 파일 입력란과 미리보기 이미지의 ID
                                function previewImage(input, previewId) {
                                    var preview = document.getElementById(previewId);
                                    
                                    //파일 입력란에서 선택된 파일 객체를 가져오기
                                    var file = input.files[0];
                                    
                                    //파일을 읽어오는데 사용되는 FileReader 객체를 생성
                                    var reader = new FileReader();
                                    
                                    //파일이 로드될 때 실행되는 이벤트 핸들러를 정의
                                    reader.onload = function (e) {
                                        preview.src = e.target.result;
                                    };
                                    
                                    //파일이 존재하는 경우에만 readAsDataURL 메서드를 사용하여
                                    //파일을 읽어와 데이터 URL로 변환
                                    if (file) {
                                        reader.readAsDataURL(file);
                                    }
                                }

                                //이벤트 핸들러를 사용하여 문서가 로드되면 아래의 코드 블록 실행
                                $(document).ready(function () {
                                
                                   //"images"로 시작하는 모든 파일 입력란에 대해 change 이벤트 핸들러를 추가
                                    $('input[name^="images"]').change(function () {
                                    
                                        //현재 파일 입력란의 ID에서 'fileInput'을 제거하여 인덱스를 추출
                                        var index = $(this).attr('id').replace('fileInput', '');
                                        
                                        //파일 입력란과 미리보기 이미지의 ID를 전달하여 previewImage 함수를 호출
                                        previewImage(this, 'previewImg' + index);
                                    });
                                });
                            </script>

 


 

🟢 상품 목록

더보기
<!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 - Product 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_productlist">상품목록</a>
                        </li>
                        <li>
                            |
                        </li>
                        <li>
                            <a href="/admin_product_insert">상품등록</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_productlist}" 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="n" th:selected="${type == 'n'}">상품명</option>
                                    <option value="nc" th:selected="${type == 'nc'}">상품명+내용</option>
                                    <option value="ca" th:selected="${type == 'ca'}">카테고리</option>
                                    <option value="s" th:selected="${type == 's'}">판매상태</option>
                                </select>

                                <!-- 검색창 -->
                                <input type="text" class="form-control" name="keyword" id="keyword" th:value="${keyword}" style="display: none;"></input>

                                <!-- 중분류(판매상태) 셀렉트 리스트 -->
                                <select class="form-select" name="categoryType" id="categoryType" style="display: none;">
                                    <option value="" th:selected="${categoryType == ''}">== 선택 ==</option>
                                    <option value="ALL" th:selected="${categoryType == 'ALL'}">전체</option>
                                    <option value="LIVING" th:selected="${categoryType == 'LIVING'}">생활용품</option>
                                    <option value="BATHROOM" th:selected="${categoryType == 'BATHROOM'}">욕실용품</option>
                                    <option value="KITCHEN" th:selected="${categoryType == 'KITCHEN'}">주방용품</option>
                                    <option value="MEMBERSALE" th:selected="${categoryType == 'MEMBERSALE'}">회원특가</option>
                                </select>

                                <!-- 중분류(판매상태) 셀렉트 리스트 -->
                                <select class="form-select" name="sellState" id="sellStateOptions" style="display: none;">
                                    <option value="" th:selected="${sellsState == ''}">== 선택 ==</option>
                                    <option value="SELL" th:selected="${sellsState == 'SELL'}">판매중</option>
                                    <option value="STOP" th:selected="${sellsState == 'STOP'}">판매중지</option>
                                    <option value="LACK" th:selected="${sellsState == 'LACK'}">재고없음</option>
                                </select>

                                <!-- 검색 버튼 -->
                                <button type="submit" class="btn btn-primary admin-dart" name="searchButton">검색</button>

                                <!-- 리셋 버튼 -->
                                <button type="reset" class="btn btn-light admin-light " name="searchButton" onclick="resetSearchForm()">다시</button>
                            </div>
                        </form>

                        <!-- JavaScript 코드 -->
                        <script>
                            document.getElementById('searchType').addEventListener('change', function () {
                                resetSearchForm();
                            });

                            function resetSearchForm() {
                                var selectedSearchType = document.getElementById('searchType').value;
                                var selectedCategoryType = document.getElementById('categoryType').value;
                                var selectedSellState = document.getElementById('sellStateOptions').value;

                                applyStylesAfterSearch(selectedSearchType, selectedCategoryType, selectedSellState);
                                toggleElementDisplay('keyword', selectedSearchType === 'n' || selectedSearchType === 'nc');
                            }

                            function applyStylesAfterSearch(searchType, categoryType, sellState) {
                                toggleElementDisplay('categoryType', searchType === 'ca');
                                toggleElementDisplay('sellStateOptions', searchType === 's');

                                // 검색 타입이 상품명 또는 상품명+내용일 때 keyword 입력 필드를 보이게 설정
                                toggleElementDisplay('keyword', searchType === 'n' || searchType === 'nc');

                                if (searchType === 'ca') {
                                    // 현재 선택된 값이 없을 때만 초기화
                                    var categoryTypeElement = document.getElementById('categoryType');
                                    if (categoryTypeElement.value === '') {
                                        // localStorage에서 이전 선택 값을 가져옴
                                        categoryTypeElement.value = localStorage.getItem('selectedCategoryType') || categoryType;
                                    }
                                }

                                // 판매 상태에 대한 유사한 로직 추가
                                var sellStateOptionsElement = document.getElementById('sellStateOptions');
                                if (searchType === 's') {
                                    // localStorage에서 이전 선택 값을 가져옴
                                    sellStateOptionsElement.value = localStorage.getItem('selectedSellState') || sellState;
                                }

                                // ... (다른 엘리먼트에 대한 유사한 로직)
                            }

                            function toggleElementDisplay(elementId, condition) {
                                var element = document.getElementById(elementId);
                                element.style.display = condition ? 'block' : 'none';
                            }

                            // 페이지 로드 시 초기 스타일 적용
                            applyStylesAfterSearch(
                                document.getElementById('searchType').value,
                                document.getElementById('categoryType').value,
                                document.getElementById('sellStateOptions').value
                            );

                            // 초기 검색창 상태 설정
                            // 검색 이후에는 초기화하지 않도록 수정
                            // resetSearchForm();

                            // 페이지 로드 시 localStorage에서 이전 선택 값을 가져와 적용
                            window.onload = function () {
                                applyStylesAfterSearch(
                                    document.getElementById('searchType').value,
                                    document.getElementById('categoryType').value,
                                    document.getElementById('sellStateOptions').value
                                );
                            }

                            // 페이지 언로드 시 localStorage에 현재 선택 값을 저장
                            window.onbeforeunload = function () {
                                localStorage.setItem('selectedCategoryType', document.getElementById('categoryType').value);
                                localStorage.setItem('selectedSellState', document.getElementById('sellStateOptions').value);
                            }
                        </script>
                    </div>
                </div>
            </div>
            <div class="row mb-20"></div>
            <div class="container">
                <div class="row">
                    <div class="col-12">
                        <div class="wishlist-table-content">
                            <div 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>
                                        <th class="width-price">판매자가</th>
                                        <th class="width-price">재고수</th>
                                        <th class="width-price">판매상태</th>
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr th:each="data:${productDTOS}">  <!--반복 영역-->

                                        <td class="product-name" th:text="${data.productId}">
                                            <h5>상품번호</h5>
                                        </td>

                                        <td class="product-name">
                                            <!-- 대표이미지 -->
                                            <th:block th:if="${data.imageDTOs != null and data.imageDTOs.size() > 0 and data.imageDTOs[0].imageFile != null}">
                                                <img th:src="|/images/item/@{${data.imageDTOs[0].imageFile}}|" width="100" height="100">
                                            </th:block>
                                        </td>


                                        <!-- 상품종류(카테고리 유형) -->
                                        <td class="product-name" th:if="${data != null}">
                                            <div th:if="${data.categoryTypeRole != null}">
                                                <div th:utext="${#strings.replace(data.categoryTypeRole.description, '\n', '&#10;')}"></div>
                                            </div>
                                            <div th:unless="${data.categoryTypeRole != null}"></div>
                                        </td>

                                        <!-- 상품명 -->
                                        <td class="product-name">
                                            <h5>
                                                <a th:href="@{/admin_product_indetail(productId=${data.productId})}" th:text="${data.productName}">상품이름</a>
                                            </h5>
                                        </td>

                                        <td class="product-name" th:text="${data.productCost}">
                                            <h5>소비자가</h5>
                                        </td>

                                        <td class="product-name" th:text="${data.productPrice}">
                                            <h5>판매가</h5>
                                        </td>

                                        <td class="product-name" th:text="${data.productCnt}">
                                            <h5>상품재고수량</h5>
                                        </td>

                                        <!-- 판매상태 -->
                                        <td class="product-name" th:if="${data != null}">
                                            <div th:if="${data.SellStateRole != null}">
                                                <div th:utext="${#strings.replace(data.SellStateRole.description, '\n', '&#10;')}"></div>
                                            </div>
                                            <div th:unless="${data.SellStateRole != null}"></div>
                                        </td>
                                    </tr>
                                    </tbody>
                                </table>

                                <!-- 페이지 번호 추가 -->
                                <div class="pagination-style text-center mt-30">
                                    <ul>
                                        <li th:unless="${startPage == 1}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=1)}">처음</a>
                                        </li>
                                        <li th:unless="${currentPage == 1}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${prevPage})}">&lt;</a>
                                        </li>

                                        <li th:each="page: ${#numbers.sequence(startPage, endPage)}">
                                            <a th:if="${currentPage != page}" th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${page})}">[[${page}]]</a>
                                            <a th:if="${currentPage == page}" href="#" class="active">[[${page}]]</a>&nbsp;
                                        </li>

                                        <li th:unless="${currentPage == lastPage}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${nextPage})}">&gt;</a>
                                        </li>
                                        <li th:unless="${endPage == lastPage}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${lastPage})}">끝</a>
                                        </li>
                                    </ul>
                                </div>
                                <!-- 페이지 번호 추가 끝 -->
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <!-- Main JS -->
    <script src="assets/js/main.js"></script>
</div>

</body>
</html>

 

▪️ 검색영역

All은 제외 → th:unless

한글로 불러오기  →  th:text

<!-- 검색 폼 -->
                        <form th:action="@{/admin_productlist}" 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="n" th:selected="${type == 'n'}">상품명</option>
                                    <option value="nc" th:selected="${type == 'nc'}">상품명+내용</option>
                                    <option value="ca" th:selected="${type == 'ca'}">카테고리</option>
                                    <option value="s" th:selected="${type == 's'}">판매상태</option>
                                </select>

                                <!-- 검색창 -->
                                <input type="text" class="form-control" name="keyword" id="keyword" th:value="${keyword}" style="display: none;"></input>

                                <!-- 중분류(판매상태) 셀렉트 리스트 -->
                                <select class="form-select" name="categoryType" id="categoryType" style="display: none;">
                                    <option value="" th:selected="${categoryType == ''}">== 선택 ==</option>
                                    <option value="ALL" th:selected="${categoryType == 'ALL'}">전체</option>
                                    <option value="LIVING" th:selected="${categoryType == 'LIVING'}">생활용품</option>
                                    <option value="BATHROOM" th:selected="${categoryType == 'BATHROOM'}">욕실용품</option>
                                    <option value="KITCHEN" th:selected="${categoryType == 'KITCHEN'}">주방용품</option>
                                    <option value="MEMBERSALE" th:selected="${categoryType == 'MEMBERSALE'}">회원특가</option>
                                </select>

                                <!-- 중분류(판매상태) 셀렉트 리스트 -->
                                <select class="form-select" name="sellState" id="sellStateOptions" style="display: none;">
                                    <option value="" th:selected="${sellsState == ''}">== 선택 ==</option>
                                    <option value="SELL" th:selected="${sellsState == 'SELL'}">판매중</option>
                                    <option value="STOP" th:selected="${sellsState == 'STOP'}">판매중지</option>
                                    <option value="LACK" th:selected="${sellsState == 'LACK'}">재고없음</option>
                                </select>

                                <!-- 검색 버튼 -->
                                <button type="submit" class="btn btn-primary admin-dart" name="searchButton">검색</button>

                                <!-- 리셋 버튼 -->
                                <button type="reset" class="btn btn-light admin-light " name="searchButton" onclick="resetSearchForm()">다시</button>
                            </div>
                        </form>

                        <!-- JavaScript 코드 -->
                        <script>
                            document.getElementById('searchType').addEventListener('change', function () {
                                resetSearchForm();
                            });

                            function resetSearchForm() {
                                var selectedSearchType = document.getElementById('searchType').value;
                                var selectedCategoryType = document.getElementById('categoryType').value;
                                var selectedSellState = document.getElementById('sellStateOptions').value;

                                applyStylesAfterSearch(selectedSearchType, selectedCategoryType, selectedSellState);
                                toggleElementDisplay('keyword', selectedSearchType === 'n' || selectedSearchType === 'nc');
                            }

                            function applyStylesAfterSearch(searchType, categoryType, sellState) {
                                toggleElementDisplay('categoryType', searchType === 'ca');
                                toggleElementDisplay('sellStateOptions', searchType === 's');

                                // 검색 타입이 상품명 또는 상품명+내용일 때 keyword 입력 필드를 보이게 설정
                                toggleElementDisplay('keyword', searchType === 'n' || searchType === 'nc');

                                if (searchType === 'ca') {
                                    // 현재 선택된 값이 없을 때만 초기화
                                    var categoryTypeElement = document.getElementById('categoryType');
                                    if (categoryTypeElement.value === '') {
                                        // localStorage에서 이전 선택 값을 가져옴
                                        categoryTypeElement.value = localStorage.getItem('selectedCategoryType') || categoryType;
                                    }
                                }

                                // 판매 상태에 대한 유사한 로직 추가
                                var sellStateOptionsElement = document.getElementById('sellStateOptions');
                                if (searchType === 's') {
                                    // localStorage에서 이전 선택 값을 가져옴
                                    sellStateOptionsElement.value = localStorage.getItem('selectedSellState') || sellState;
                                }

                                // ... (다른 엘리먼트에 대한 유사한 로직)
                            }

                            function toggleElementDisplay(elementId, condition) {
                                var element = document.getElementById(elementId);
                                element.style.display = condition ? 'block' : 'none';
                            }

                            // 페이지 로드 시 초기 스타일 적용
                            applyStylesAfterSearch(
                                document.getElementById('searchType').value,
                                document.getElementById('categoryType').value,
                                document.getElementById('sellStateOptions').value
                            );

                            // 초기 검색창 상태 설정
                            // 검색 이후에는 초기화하지 않도록 수정
                            // resetSearchForm();

                            // 페이지 로드 시 localStorage에서 이전 선택 값을 가져와 적용
                            window.onload = function () {
                                applyStylesAfterSearch(
                                    document.getElementById('searchType').value,
                                    document.getElementById('categoryType').value,
                                    document.getElementById('sellStateOptions').value
                                );
                            }

                            // 페이지 언로드 시 localStorage에 현재 선택 값을 저장
                            window.onbeforeunload = function () {
                                localStorage.setItem('selectedCategoryType', document.getElementById('categoryType').value);
                                localStorage.setItem('selectedSellState', document.getElementById('sellStateOptions').value);
                            }
                        </script>

 

 

▪️ 이미지 불러오기

th:block →  조건이 참일 때만 내부의 코드 블록이 실행

 

이미지의 소스(src) 속성을 동적으로 설정

th:src= /이미지 폴더 위치 /@{${변수}}

                                            <th:block th:if="${data.imageDTOs != null and data.imageDTOs.size() > 0 and data.imageDTOs[0].imageFile != null}">
                                                <img th:src="|/images/item/@{${data.imageDTOs[0].imageFile}}|" width="100" height="100">
                                            </th:block>

 

 

▪️ 카테고리 불러오기

                                        <td class="product-name" th:if="${data != null}">
                                            <div th:if="${data.categoryTypeRole != null}">
                                                <div th:text="${data.categoryTypeRole.description}"></div>
                                            </div>
                                            <div th:unless="${data.categoryTypeRole != null}"></div>
                                        </td>

 

 

▪️ 페이지네이션

startPage, currentPage, lastPage, endPage

#numbers.sequence( startPage, endPage)

                                <!-- 페이지 번호 추가 -->
                                <div class="pagination-style text-center mt-30">
                                    <ul>
                                        <li th:unless="${startPage == 1}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=1)}">처음</a>
                                        </li>
                                        <li th:unless="${currentPage == 1}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${prevPage})}">&lt;</a>
                                        </li>

                                        <li th:each="page: ${#numbers.sequence(startPage, endPage)}">
                                            <a th:if="${currentPage != page}" th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${page})}">[[${page}]]</a>
                                            <a th:if="${currentPage == page}" href="#" class="active">[[${page}]]</a>&nbsp;
                                        </li>

                                        <li th:unless="${currentPage == lastPage}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${nextPage})}">&gt;</a>
                                        </li>
                                        <li th:unless="${endPage == lastPage}">
                                            <a th:href="@{/admin_productlist(type=${type}, keyword=${keyword}, page=${lastPage})}">끝</a>
                                        </li>
                                    </ul>
                                </div>
                                <!-- 페이지 번호 추가 끝 -->

 

 


 

🟢 상품 상세

더보기
<!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 - Product Detail</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 Detail</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" th:object="${productDTO}">
                        <tbody>
                        <tr>
                            <th scope="row" class="notice-padding" id="productName">상품명</th>
                            <td class="notice-info">
                                <input type="text" name="productName" th:field="*{productName}" readonly>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row" class="notice-padding" id="productContent">상품설명</th>
                            <td class="notice-info">
                                <input type="text" name="productContent" th:field="*{productContent}" readonly>
                            </td>
                        </tr>

                        <tr>
                            <th scope="row" class="notice-padding" id="CategoryTypeRole">카테고리</th>
                            <td class="notice-info">
                                <div class="sidebar-widget update">
                                    <div class="sidebar-archive-wrap">
                                        <!-- null 체크 추가 -->
                                        <input type="text" th:if="${productDTO.categoryTypeRole != null}" th:value="${productDTO.categoryTypeRole.getDescription()}" readonly>
                                        <input type="text" th:if="${productDTO.categoryTypeRole == null}" value="카테고리 없음" readonly>
                                    </div>
                                </div>
                            </td>
                        </tr>

                        <tr>
                            <th scope="row" class="notice-padding" id="productCost">소비자가</th>
                            <td class="notice-info">
                                <input type="text" name="productCost" th:field="*{productCost}" readonly>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row" class="notice-padding" id="productPrice">판매가</th>
                            <td class="notice-info">
                                <input type="text" name="productPrice" th:field="*{productPrice}" readonly>
                            </td>
                        </tr>
                        <tr>
                            <th scope="row" class="notice-padding" id="productCnt">재고수</th>
                            <td class="notice-info">
                                <input type="number" name="productCnt" th:field="*{productCnt}" readonly>
                            </td>
                        </tr>

                        <tr>
                            <th scope="row" class="notice-padding" id="SellStateRole">판매상태</th>
                            <td class="notice-info">
                                <div class="sidebar-widget update">
                                    <div class="sidebar-archive-wrap">
                                        <!-- null 체크 추가 -->
                                        <input type="text" th:if="${productDTO.sellStateRole != null}" th:value="${productDTO.sellStateRole.getDescription()}" readonly>
                                        <input type="text" th:if="${productDTO.sellStateRole == null}" value="판매상태 없음" readonly>
                                    </div>
                                </div>
                            </td>
                        </tr>

                        <tr>
                            <th scope="row" class="notice-padding">등록일</th>
                            <td class="notice-info">
                                <input type="text" name="reDate" th:value="${#temporals.format(productDTO.reDate, 'yyyy-MM-dd')}" readonly>
                            </td>
                        </tr>

                        <tr>
                            <th scope="row" class="notice-padding">수정일</th>
                            <td class="notice-info">
                                <input type="text" name="modDate" th:value="${#temporals.format(productDTO.modDate, 'yyyy-MM-dd HH:mm:ss')}" readonly>
                            </td>
                        </tr>

                        <tr>
                            <th scope="row" class="notice-padding" id="productDetail">상품정보</th>
                            <td class="notice-info">
                                <textarea rows="7" name="productDetail" th:field="*{productDetail}" readonly></textarea>
                            </td>
                        </tr>

                        <!-- 대표 이미지 -->
                        <tr th:if="${#lists.size(productDTO.imageDTOs) > 0}">
                            <th scope="row" class="notice-padding" id="imageDTOs[0]">대표이미지</th>
                            <td class="notice-info">
                                <!-- 이미지가 있는 경우에만 표시 -->
                                <img th:if="${productDTO.imageDTOs[0] != null}"
                                     th:src="|/images/item/@{${productDTO.imageDTOs[0].imageFile}}|"
                                     onerror="this.onerror=null; this.src='../assets/images/product/null.png';" width="300" height="300">
                            </td>
                        </tr>


                        <!-- 서브 이미지 -->
                        <tr th:if="${#lists.size(productDTO.imageDTOs) > 1}">
                            <th scope="row" class="notice-padding" id="imageDTOs[1]">서브이미지</th>
                            <td class="notice-info">
                                <!-- 이미지가 있는 경우에만 표시 -->
                                <img th:if="${productDTO.imageDTOs[1] != null}"
                                     th:src="|/images/item/@{${productDTO.imageDTOs[1].imageFile}}|"
                                     onerror="this.onerror=null; this.src='../assets/images/product/null.png';"  width="300" height="300">
                            </td>
                        </tr>

                        <!-- 상세 이미지 -->
                        <tr th:if="${#lists.size(productDTO.imageDTOs) > 2}">
                            <th scope="row" class="notice-padding" id="imageDTOs[2]">상세이미지</th>
                            <td class="notice-info">
                                <img th:if="${productDTO.imageDTOs[2] != null}"
                                     th:src="|/images/item/@{${productDTO.imageDTOs[2].imageFile}}|"
                                     onerror="this.onerror=null; this.src='../assets/images/product/null.png';" style="width:50%;">
                            </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_productlist}'|">목록</button>
                                            <button type="button" class="btn btn-lg" th:onclick="|location.href='@{/admin_product_update(productId=${productDTO.productId})}'|">수정</button>
                                            <button type="button" class="btn btn-lg" th:onclick="|location.href='@{/admin_product_delete(productId=${productDTO.productId})}'|">삭제</button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div><!-- content 끝-->
</body>
</html>

▪️ 카테고리 불러오기

null인 경우 : value 설정

null 아닌 경우 : getDescription 한글로 받아오기

<input type="text" th:if="${productDTO.categoryTypeRole != null}" th:value="${productDTO.categoryTypeRole.getDescription()}" readonly>
<input type="text" th:if="${productDTO.categoryTypeRole == null}" value="카테고리 없음" readonly>

 

▪️ 이미지 불러오기

                        <tr th:if="${#lists.size(productDTO.imageDTOs) > 0}">
                            <th scope="row" class="notice-padding" id="imageDTOs[0]">대표이미지</th>
                            <td class="notice-info">
                                <!-- 이미지가 있는 경우에만 표시 -->
                                <img th:if="${productDTO.imageDTOs[0] != null}"
                                     th:src="|/images/item/@{${productDTO.imageDTOs[0].imageFile}}|"
                                     onerror="this.onerror=null; this.src='../assets/images/product/null.png';" width="300" height="300">
                            </td>
                        </tr>

 


 

🟢 상품 수정

더보기
<!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 - Product 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>product Update</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_product_update'}" method="post" enctype="multipart/form-data" th:object="${productDTO}">

            <input type="hidden" name="productId" th:field="*{productId}">
            <input type="hidden" th:field="*{modDate}" name="modDate">
            <table class="table notice-table">

              <tbody>
              <tr>
                <th scope="row" class="notice-padding" id="productName">상품명</th>
                <td class="notice-info">
                  <input type="text" name="productName" th:field="*{productName}">
                </td>
              </tr>
              <tr>
                <th scope="row" class="notice-padding" id="productContent">상품설명</th>
                <td class="notice-info">
                  <input type="text" name="productContent" th:field="*{productContent}">
                </td>
              </tr>
              <tr>
                <th scope="row" class="notice-padding" id="CategoryTypeRole">카테고리</th>
                <td class="notice-info">
                  <div class="sidebar-widget update">
                    <div class="sidebar-archive-wrap">
                      <select name="categoryTypeRole">
                        <option th:each="state : ${categoryType}"
                                th:value="${state.name()}"
                                th:text="${state.getDescription()}"
                                th:selected="${state.name() eq productDTO.categoryTypeRole?.name()}">
                        </option>
                      </select>
                    </div>
                  </div>
                </td>
              </tr>
              <tr>
                <th scope="row" class="notice-padding">소비자가</th>
                <td class="notice-info">
                  <input type="text" name="productCost" th:field="*{productCost}">
                </td>
              </tr>
              <tr>
                <th scope="row" class="notice-padding" id="productPrice">판매가</th>
                <td class="notice-info">
                  <input type="text" name="productPrice" th:field="*{productPrice}">
                </td>
              </tr>
              <tr>
                <th scope="row" class="notice-padding" id="productCnt">재고수</th>
                <td class="notice-info">
                  <input type="number" name="productCnt" th:field="*{productCnt}">
                </td>
              </tr>

              <!-- 판매상태 -->
              <tr>
                <th scope="row" class="notice-padding" id="SellStateRole">판매상태</th>
                <td class="notice-info">
                  <div class="sidebar-widget update">
                    <div class="sidebar-archive-wrap">
                      <!---수정-->
                      <select id="sellState" name="sellStateRole">
                        <!-- 열거형 값들을 반복하며 옵션을 생성 -->
                        <option th:each="state : ${sellState}"
                                th:value="${state.name()}"
                                th:text="${state.getDescription()}"
                                th:selected="${state.name() eq productDTO.sellStateRole?.name()}">
                        </option>
                      </select>
                      <!--수정끝-->
                    </div>
                  </div>
                </td>
              </tr>

              <tr>
                <th scope="row" class="notice-padding" id="productDetail">상품정보</th>
                <td class="notice-info">
                  <textarea rows="7" name="productDetail" th:field="*{productDetail}"></textarea>
                </td>
              </tr>

              <!-- 대표 이미지 -->
              <tr>
                <th scope="row" class="notice-padding" id="imageDTOs[0]">대표이미지</th>
                <td class="notice-info">
                  <input type="hidden" name="imageDTOs[0].imageId" th:value="*{imageDTOs[0].imageId}">
                  <input type="hidden" name="imageDTOs[0].imageType" value="0">
                  <input type="file" name="images" onchange="previewImage(this, 'previewImg0')">
                  <img id="previewImg0" class="img-thumbnail" style="max-width: 100px; max-height: 100px;" th:if="${imageDTOs[0].imageFile != null}" th:src="|/images/item/${imageDTOs[0].imageFile}|">
                </td>
              </tr>

              <!-- 서브 이미지 -->
              <tr>
                <th scope="row" class="notice-padding" id="imageDTOs[0]">서브이미지</th>
                <td class="notice-info">
                  <input type="hidden" name="imageDTOs[1].imageId" th:value="*{imageDTOs[1].imageId}">
                  <input type="hidden" name="imageDTOs[1].imageType" value="1">
                  <input type="file" name="images" onchange="previewImage(this, 'previewImg1')">
                  <img id="previewImg1" class="img-thumbnail" style="max-width: 100px; max-height: 100px;" th:if="${imageDTOs[1].imageFile != null}" th:src="|/images/item/${imageDTOs[1].imageFile}|">
                </td>
              </tr>

              <!-- 상세 이미지 -->
              <tr>
                <th scope="row" class="notice-padding" id="imageDTOs[2]">상세이미지</th>
                <td class="notice-info">
                  <input type="hidden" name="imageDTOs[2].imageId" th:value="*{imageDTOs[2].imageId}">
                  <input type="hidden" name="imageDTOs[2].imageType" value="2">
                  <input type="file" name="images" onchange="previewImage(this, 'previewImg2')">
                  <img id="previewImg2" class="img-thumbnail" style="max-width: 100px; max-height: 100px;" th:if="${imageDTOs[2].imageFile != null}" th:src="|/images/item/${imageDTOs[2].imageFile}|">
                </td>
              </tr>

              <script>
                  // 미리보기 이미지 업데이트 함수
                  function previewImage(input, imgId) {
                      var preview = document.getElementById(imgId);
                      var file = input.files[0];
                      var reader = new FileReader();

                      reader.onloadend = function () {
                          preview.src = reader.result;
                      };

                      if (file) {
                          reader.readAsDataURL(file);
                      } else {
                          preview.src = "";
                      }
                  }
              </script>

              </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_productlist}'|">목록</button>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
</div><!-- content 끝-->
</body>
</html>

 

▪️ 카테고리 설정

                      <select name="categoryTypeRole">
                        <option th:each="state : ${categoryType}"
                                th:value="${state.name()}"
                                th:text="${state.getDescription()}"
                                th:selected="${state.name() eq productDTO.categoryTypeRole?.name()}">
                        </option>
                      </select>

 

▪️ 이미지 설정

                            <!-- 대표 이미지 -->
                            <tr>
                                <th scope="row" class="notice-padding" id="imageDTOs[0]">대표이미지</th>
                                <td class="notice-info">
                                    <input type="hidden" name="imageDTOs[0].imageType" value="0">
                                    <input type="file" name="images" onchange="previewImage(this, 'previewImg0')">
                                    <img id="previewImg0" class="img-thumbnail" style="max-width: 100px; max-height: 100px;">
                                </td>
                            </tr>
                            <script>
                                function previewImage(input, previewId) {
                                    var preview = document.getElementById(previewId);
                                    var file = input.files[0];
                                    var reader = new FileReader();

                                    reader.onload = function (e) {
                                        preview.src = e.target.result;
                                    };

                                    if (file) {
                                        reader.readAsDataURL(file);
                                    }
                                }

                                $(document).ready(function () {
                                    $('input[name^="images"]').change(function () {
                                        var index = $(this).attr('id').replace('fileInput', '');
                                        previewImage(this, 'previewImg' + index);
                                    });
                                });
                            </script>

 

반응형