[User_상품진열] enum 카테고리별로 진열 / 상품 상세페이지 / 할인율 계산 / 자동합산 / 연관상품

2024. 1. 20. 15:32Project/ShoppingMall

반응형

 

 

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

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

dalhyehye.tistory.com

 

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

[Admin_상품등록] 다중 이미지 삽입 / 이미지-상품 테이블 조인 / enum 카테고리, 판매 상태(1) 🟢 Constant 열거형 : 고정된 값들의 집합 ▪️ CategoryTypeRole 제품 카테고리 설정 CategoryTypeRole이라는 열거

dalhyehye.tistory.com

 

🟢 Repository

▪️ ProductRepository

상품진열 : 카테고리별로 진열 / SELL 상태인 재품만 진열

 

<> 연산자 : "제외" 또는 "같지 않음"을 나타냄

@Repository
public interface ProductRepository extends JpaRepository<ProductEntity, Integer> {

  //상품 고유번호
  ProductEntity findByProductId(Integer productId);

  //상품진열
  //연관상품(특정 상품 아이디와 동일한 카테고리에 속하는 제품, 주어진 상품 아이디와 다른 상품들을 검색)
  @Query("SELECT p FROM ProductEntity p WHERE p.productId <> :productId AND p.categoryTypeRole = :categoryTypeRole")
  List<ProductEntity> findRelatedProductsByCategoryTypeRole(@Param("productId") Integer productId, @Param("categoryTypeRole") CategoryTypeRole categoryTypeRole);

  //카테고리, 판매상태
  @Query("SELECT p FROM ProductEntity p WHERE p.categoryTypeRole = :categoryTypeRole AND p.sellStateRole = :sellStateRole")
  Page<ProductEntity> findProductEntityByCategoryTypeRoleAndSellState (@Param("categoryTypeRole") CategoryTypeRole categoryTypeRole, @Param("sellStateRole") SellStateRole sellStateRole, Pageable pageable);

  //All 카테고리 (MEMBERSALE 아니면서 SELL 상태의 모든 상품)
  @Query("SELECT p FROM ProductEntity p WHERE p.categoryTypeRole <> :MEMBERSALE AND p.sellStateRole = :SELL")
  Page<ProductEntity> findByCategoryTypeRoleNotAndSellStateRole(@Param("MEMBERSALE") CategoryTypeRole MEMBERSALE, @Param("SELL") SellStateRole SELL, Pageable pageable);

}

 

 

🟢 Service

▪️ ProductService

상품진열 : 카테고리별로 진열 / ALL에는 모든 상품 진열 (단, Membersale 제외, SELL 상태만 재품만 진열)

 

- ALL 경우 : MEMBERSALE 카테고리를 제외하고, SELL 상태에 있는 상품들을 페이징하여 가져오기
- ALL 아닌 경우 : 주어진 categoryTypeRole과 SELL 상태에 있는 상품들을 페이징하여 가져오기


- 쿼리의 결과는 Page<ProductEntity> 객체에 담겨서 반환
- 페이지 번호, 페이지 크기 등 페이징 관련 정보는 pageable 파라미터를 통해 전달

@Service
@RequiredArgsConstructor
@Transactional
public class ProductService {

  private final FileService fileService;
  private final ImageService imageService;
  private final ModelMapper modelMapper = new ModelMapper();

  private final ProductRepository productRepository;

  //카테고리 별로 상품 목록 조회 (검색 포함, 페이징 처리) -> 회원용
  public Page<ProductDTO> categoryList (String categoryTypeRole, Pageable page) throws Exception {

    int currentPage = page.getPageNumber() -1;
    int itemsPerPage = 8;

    //Pageable 객체 : 제품번호를 기준으로 내림차순으로 정렬
    Pageable pageable = PageRequest.of(currentPage, itemsPerPage, Sort.by(Sort.Direction.DESC, "productId"));


    //진열조건
    Page<ProductEntity> productEntityPage;
    if ("ALL".equalsIgnoreCase(categoryTypeRole)) {
      productEntityPage = productRepository.findByCategoryTypeRoleNotAndSellStateRole(CategoryTypeRole.MEMBERSALE, SellStateRole.SELL, pageable);
    } else {
      // 다른 카테고리의 경우 해당 카테고리의 상품을 조회
      productEntityPage = productRepository.findProductEntityByCategoryTypeRoleAndSellState(CategoryTypeRole.valueOf(categoryTypeRole), SellStateRole.SELL, pageable);
    }


    //상품정보 및 이미지들을 Entity에서 DTO로 복수전달 (조회된 ProductEntity 목록을 순회하면서 각 엔터티를 ProductDTO로 변환)
    //ProductDTO 객체를 담기 위한 ArrayList를 생성하고, 이후에 상품 정보를 ProductDTO로 변환하여 리스트에 추가할 예정
    List<ProductDTO> productDTOList = new ArrayList<>();

    for (ProductEntity entity : productEntityPage) {
      ProductDTO productDTO = ProductDTO.builder()
          .productId(entity.getProductId())
          .productName(entity.getProductName())
          .productContent(entity.getProductContent())
          .productDetail(entity.getProductDetail())
          .productCost(entity.getProductCost())
          .productPrice(entity.getProductPrice())
          .productDis(entity.getProductDis())
          .productOpt(entity.getProductOpt())
          .productCnt(entity.getProductCnt())
          .productLike(entity.getProductLike())
          .productViewcnt(entity.getProductViewcnt())
          .categoryTypeRole(entity.getCategoryTypeRole())
          .sellStateRole(entity.getSellStateRole())
          .reDate(entity.getReDate())
          .modDate(entity.getModDate())
          .imageDTOs(mapImagesToDTOs(entity.getProductImages()))
          .build();

      productDTOList.add(productDTO);
    }

    //PageImpl 클래스 : 페이징된 결과를 반환 (productDTOList에 변환된 DTO를 추가, 전체 엔터티의 총 개수, 페이지 정보)
    return new PageImpl<>(productDTOList, pageable, productEntityPage.getTotalElements());
  }

}
더보기
  //각 상품에 이미지테이블 전달
  private List<ImageDTO> mapImagesToDTOs(List<ImageEntity> imagesEntities) {

    //이미지가 없는 경우, 빈 리스트를 반환
    if (imagesEntities == null) {
      return Collections.emptyList();
    }

    //이미지 목록이 null이 아닌 경우
    //이미지 목록을 스트림으로 변환, map 함수를 사용하여 각 ImageEntity를 ImageDTO로 매핑
    //collect(Collectors.toList())를 사용하여 스트림의 결과를 리스트로 수집
    return imagesEntities.stream()
        .map(imageEntity -> modelMapper.map(imageEntity, ImageDTO.class))
        .collect(Collectors.toList());
  }

상품개별조회

  //상품개별조회
  public ProductDTO findOne(Integer productId) throws Exception {

    //modelMapper를 사용하여 엔터티와 DTO 간의 매핑을 수행
    //이미지 관련 필드를 제외하도록 설정 ( 이미지 정보는 별도로 처리할 것이기 때문 )
    modelMapper.typeMap(ProductEntity.class, ProductDTO.class)
        .addMappings(mapper -> mapper.skip(ProductDTO::setImages));

    //Optional에서 데이터를 추출하기 위해 get() 메서드를 사용
    Optional<ProductEntity> data = productRepository.findById(productId);
    ProductEntity entity = data.get();

    //변환
    //Optional에 데이터가 있는 경우, 해당 데이터를 ProductDTO로 변환 (images 필드는 무시)
    ProductDTO result = data.map(mapper -> modelMapper.map(mapper, ProductDTO.class)).orElse(null);

    //상품에 속한 이미지들을 가져오기
    //각 이미지를 ImageDTO로 변환하고, 이를 리스트로 수집
    List<ImageDTO> imageDTOS = entity.getProductImages().stream()
        .map(imageEntity -> modelMapper.map(imageEntity, ImageDTO.class))
        .collect(Collectors.toList());
    result.setImageDTOs(imageDTOS);

    return result;
  }

 

🟢 Controller

▪️ ProductController

상품 목록

@Controller
@RequiredArgsConstructor
@Log4j2
public class MemberProductController {

  @Value("${uploadPath}")
  private String uploadPath;

  @Value("${imgLocation}")
  private String imgLocation;

  private final ProductService productService;
  private final ProductRepository productRepository;

  //회원 제품 목록 - (카테고리별로 진열)
  @GetMapping("/productlist/{categoryTypeRole}")
  public String productlistForm(@PathVariable(value = "categoryTypeRole") String categoryTypeRole,
                                @PageableDefault(page = 1) Pageable pageable,
                                Model model) throws Exception {

    Page<ProductDTO> productDTOS = productService.categoryList(categoryTypeRole, pageable);

    int blockLimit = 5;
    int startPage = (((int) (Math.ceil((double) pageable.getPageNumber() / blockLimit))) - 1) * blockLimit + 1;
    int endPage = Math.min(startPage + blockLimit - 1, productDTOS.getTotalPages());

    int prevPage = productDTOS.getNumber();
    int currentPage = productDTOS.getNumber() + 1;
    int nextPage = productDTOS.getNumber() + 2;
    int lastPage = productDTOS.getTotalPages();

    model.addAttribute("productDTOS", productDTOS);

    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("totalElements", productDTOS.getTotalElements());

    //해당 변수의 값을 사용해 동적으로 뷰 이름을 생성
    return "product/" + categoryTypeRole;
    
  }

}

 

상세페이지

연관상품도 함께 진열

@Controller
@RequiredArgsConstructor
@Log4j2
public class MemberProductController {

  @Value("${uploadPath}")
  private String uploadPath;

  @Value("${imgLocation}")
  private String imgLocation;

  private final ProductService productService;
  private final ProductRepository productRepository;

  // 제품 상세페이지
  //@PathVariable : URL 경로에서 변수 값을 추출하는 데 사용
  @GetMapping("/product/{productId}")
  public String memProDetail(@PathVariable Integer productId,
                             Model model) throws Exception {

    ProductDTO productDTO = productService.findOne(productId);

    //연관상품
    List<ProductEntity> relatedProducts = productRepository.findRelatedProductsByCategoryTypeRole(productId, productDTO.getCategoryTypeRole());
    model.addAttribute("relatedProducts",relatedProducts);
    
    model.addAttribute("productDTO", productDTO);
    model.addAttribute("sellState", SellStateRole.values());
    model.addAttribute("categoryType", CategoryTypeRole.values());
    model.addAttribute("imageDTOs", productDTO.getImageDTOs());

    return "/product/productdetail";
  }

}

 


 

 

🟢 All.html

더보기
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
      layout:decorate="~{layouts/layout}">

<head>
    <meta charset="UTF-8">
    <title>Product list</title>
</head>

<body>
<div layout:fragment="content">
    <div class="main-wrapper wrapper-2">
        <div class="breadcrumb-area breadcrumb-padding">
            <div class="container">
                <div class="breadcrumb-content text-center">
                    <div class="breadcrumb-title" data-aos="fade-up" data-aos-delay="200">
                        <h2>Clean Product</h2>
                    </div>
                    <ul data-aos="fade-up" data-aos-delay="300">
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/ALL' ? 'active' : ''}">
                            <a href="/productlist/ALL">ALL</a>
                        </li>
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/LIVING' ? 'active' : ''}">
                            <a href="/productlist/LIVING">LIVING</a>
                        </li>
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/KITCHEN' ? 'active' : ''}">
                            <a href="/productlist/KITCHEN">KITCHEN</a>
                        </li>
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/BATHROOM' ? 'active' : ''}">
                            <a href="/productlist/BATHROOM">BATHROOM</a>
                        </li>
                        <!--인증처리된 사용자만 접근가능-->
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/MEMBERSALE' ? 'active' : ''}"
                            sec:authorize="isAuthenticated()">
                            <a href="/productlist/MEMBERSALE">SALE</a>
                        </li>
                    </ul>
                </div>
            </div>
        </div>

        <div class="shop-area section-padding-lr-1 pb-235">
            <div class="container-fluid">
                <div class="shop-page-wrap margin-none">
                    <div class="shop-top-bar shop-top-bar-responsive">
                        <div class="show-wrap" data-aos="fade-up" data-aos-delay="200">
                            Total <b><span th:text="${totalElements}">0</span></b> <!-- 전체 상품 수 -->
                        </div>

                        <div class="sorting-filter-wrap">
                            <div class="shop-sorting-area">
                                <select class="nice-select nice-select-style-2">
                                    <option>인기순</option>
                                    <option>신상품</option>
                                    <option>낮은 가격</option>
                                    <option>조회수</option>
                                </select>
                            </div>
                        </div>

                    </div>
                    <div class="padding-54-row-col">
                        <div class="row">
                            <!-- 상품 목록 생성 -->
                            <div th:each="product:${productDTOS}" class="col-xl-3 col-lg-4 col-md-6 col-sm-6 col-12 mb-75"
                                 th:if="${product.sellStateRole == T(com.example.clean.Constant.SellStateRole).SELL}">
                                <div class="product-wrap" data-aos="fade-up" data-aos-delay="${product.delay}">
                                    <div class="product-img img-zoom mb-4">
                                        <a th:href="@{/product/{productId}(productId=${product.productId})}">
                                            <img class="default-img" th:if="${not product.imageDTOs?.isEmpty()}" th:src="@{/images/item/{imageName}(imageName=${product.imageDTOs[0]?.imageFile})}" width="600" height="300">
                                            <img class="default-img" th:if="${product.imageDTOs?.isEmpty()}" th:src="@{/images/item/default-image.jpg}" width="600" height="300">
                                        </a>
                                            <div class="product-action-wrap">
                                                <button title="Add To Cart">
                                                    <img class="injectable" src="assets/images/icon-img/bag-2.svg" alt="" />
                                                </button>
                                                <button title="Wishlist">
                                                    <img class="injectable" src="assets/images/icon-img/heart.svg" alt="" />
                                                </button>
                                            </div>
                                    </div>
                                    <div class="product-content text-center">
                                        <h3>
                                            <a th:href="@{/product/{productId}(productId=${product.productId})}" th:text="${product.productName}" />
                                        </h3>
                                        <div class="product-price">
                                            <span th:text="${product.productPrice}" />
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <!-- 반복문 끝 -->
                        </div>
                    </div>

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

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

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

 

▪️ 선택 카테고리 활성화

sec:authorize="isAuthenticated()" → 접근 제한 설정

                    <ul data-aos="fade-up" data-aos-delay="300">
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/ALL' ? 'active' : ''}">
                            <a href="/productlist/ALL">ALL</a>
                        </li>
                        
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/LIVING' ? 'active' : ''}">
                            <a href="/productlist/LIVING">LIVING</a>
                        </li>
                        
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/KITCHEN' ? 'active' : ''}">
                            <a href="/productlist/KITCHEN">KITCHEN</a>
                        </li>
                        
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/BATHROOM' ? 'active' : ''}">
                            <a href="/productlist/BATHROOM">BATHROOM</a>
                        </li>
                        
                        <!--인증처리된 사용자만 접근가능-->
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/MEMBERSALE' ? 'active' : ''}"
                            sec:authorize="isAuthenticated()">
                            <a href="/productlist/MEMBERSALE">SALE</a>
                        </li>
                    </ul>

 

 

▪️ 상품 목록

SELL 상태만 제품만 진열

                            <div th:each="product:${productDTOS}" class="col-xl-3 col-lg-4 col-md-6 col-sm-6 col-12 mb-75"
                                 th:if="${product.sellStateRole == T(com.example.clean.Constant.SellStateRole).SELL}">
                                <div class="product-wrap" data-aos="fade-up" data-aos-delay="${product.delay}">
                                    <div class="product-img img-zoom mb-4">
                                        <a th:href="@{/product/{productId}(productId=${product.productId})}">
                                            <img class="default-img" th:if="${not product.imageDTOs?.isEmpty()}" th:src="@{/images/item/{imageName}(imageName=${product.imageDTOs[0]?.imageFile})}" width="600" height="300">
                                            <img class="default-img" th:if="${product.imageDTOs?.isEmpty()}" th:src="@{/images/item/default-image.jpg}" width="600" height="300">
                                        </a>
                                    </div>
                                    <div class="product-content text-center">
                                        <h3>
                                            <a th:href="@{/product/{productId}(productId=${product.productId})}" th:text="${product.productName}" />
                                        </h3>
                                        <div class="product-price">
                                            <span th:text="${product.productPrice}" />
                                        </div>
                                    </div>
                                </div>
                            </div>
더보기

이미지가 있는 경우

<img class="default-img" th:if="${not product.imageDTOs?.isEmpty()}"

  th:src="@{/images/item/{imageName}(imageName=${product.imageDTOs[0]?.imageFile})}" width="600" height="300">

 

이미지가 없는 경우
<img class="default-img" th:if="${product.imageDTOs?.isEmpty()}"

  th:src="@{/images/item/default-image.jpg}" width="600" height="300">

 

▪️ 페이지네이션

                    <div class="pagination-style text-center mt-30" th:if="${lastPage > 1}">
                        <ul>
                            <li th:unless="${startPage == 1}">
                                <a th:href="@{/productlist/ALL(page=1)}">처음</a>
                            </li>
                            <li th:unless="${currentPage == 1}">
                                <a th:href="@{/productlist/ALL(page=${prevPage})}">&lt;</a>
                            </li>

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

                            <li th:unless="${currentPage == lastPage}">
                                <a th:href="@{/productlist/ALL(page=${nextPage})}">&gt;</a>
                            </li>
                            <li th:unless="${endPage == lastPage}">
                                <a th:href="@{/productlist/ALL(page=${lastPage})}">끝</a>
                            </li>
                        </ul>
                    </div>

 

 

🟢 detatil.html

더보기
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
      layout:decorate="~{layouts/layout}">

<head>
    <meta charset="UTF-8">
    <title>Product list</title>
</head>

<body>
<div layout:fragment="content">
    <div class="main-wrapper wrapper-2">
        <div class="breadcrumb-area breadcrumb-padding">
            <div class="container">
                <div class="breadcrumb-content text-center">
                    <div class="breadcrumb-title" data-aos="fade-up" data-aos-delay="200">
                        <h2>Clean Product</h2>
                    </div>
                    <ul data-aos="fade-up" data-aos-delay="300">
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/ALL' ? 'active' : ''}">
                            <a href="/productlist/ALL">ALL</a>
                        </li>
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/LIVING' ? 'active' : ''}">
                            <a href="/productlist/LIVING">LIVING</a>
                        </li>
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/KITCHEN' ? 'active' : ''}">
                            <a href="/productlist/KITCHEN">KITCHEN</a>
                        </li>
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/BATHROOM' ? 'active' : ''}">
                            <a href="/productlist/BATHROOM">BATHROOM</a>
                        </li>
                        <!--인증처리된 사용자만 접근가능-->
                        <li th:classappend="${#httpServletRequest.requestURI == '/productlist/MEMBERSALE' ? 'active' : ''}"
                            sec:authorize="isAuthenticated()">
                            <a href="/productlist/MEMBERSALE">SALE</a>
                        </li>
                    </ul>
                </div>
            </div>
        </div>

        <form th:action="@{/order?productId=${productId}}" method="get">
            <input type="hidden" id="productId" name="productId" th:value="${productId}">   <!-- 상품 번호 전달 -->
            <div class="product-details-area section-padding-lr-2 pb-115">
                <div class="container-fluid">
                    <div class="back-to-shop" data-aos="fade-up" data-aos-delay="200">
                        <a href="/productlist"><img class="injectable" src="assets/images/icon-img/arrow-left-6.svg" alt=""> Back to Shop</a>
                    </div>
                    <div class="row">
                        <!-- 대표 이미지 섹션 수정 -->
                        <div class="col-lg-7">
                            <div class="product-details-tab" data-aos="fade-up" data-aos-delay="300">
                                <div class="pro-dec-big-img-slider">
                                <!-- 큰 이미지-->
                                    <div class="easyzoom-style" th:each="image, iterStat : ${productDTO.imageDTOs}">
                                        <div class="easyzoom easyzoom--overlay d-flex justify-content-center">
                                            <div th:href="@{/images/item/{imageDTOs}(imageDTOs=${image.imageFile})}">
                                                <img th:src="@{/images/item/{imageDTOs}(imageDTOs=${image.imageFile})}"
                                                     style="width: 530px; height: 530px; object-fit: cover;">
                                            </div>
                                        </div>
                                    </div>
                                </div>

                                <div class="product-dec-slider-small product-dec-small-style1"> <!-- 서브 이미지 -->
                                    <div class="product-dec-small" th:each="image, iterStat : ${productDTO.imageDTOs}"
                                         th:if="${iterStat.index != 2}"
                                         th:classappend="${iterStat.index == 0} ? 'active'"> <!--productDTO 이미지의 2번째까지만 노출-->
                                        <img th:src="@{/images/item/{filename}(filename=${image.imageFile})}">
                                    </div>
                                </div>
                            </div>
                        </div>

                        <!--상품설명-->
                        <div class="col-lg-5">
                            <div class="product-details-content product-details-mrg-left">
                                <b><h2 data-aos="fade-up" data-aos-delay="300" th:text="${productDTO.productName}"></h2></b>
                                <p data-aos="fade-up" data-aos-delay="500" th:text="${productDTO.productContent}"></p>
                                <hr>
                                <div class="product-details-price" data-aos="fade-up" data-aos-delay="400">
                                    <table class="table table-borderless">
                                        <tbody>
                                        <tr class="product-consumer-price">
                                            <th scope="row">소비자가</th>
                                            <td id="productCost" th:text="${productDTO.productCost}"></td>
                                        </tr>

                                        <tr class="product-price">
                                            <th scope="row">판매가</th>
                                            <td id="productPrice" th:text="${productDTO.productPrice}"></td>
                                        </tr>

                                        <tr class="product-price">
                                            <th scope="row">할인율</th>
                                            <td id="productDis" th:text="${productDTO.productDis}"></td>
                                        </tr>

                                        <script>
                                            // 서버에서 전달된 소비자가와 판매가를 가져오기
                                            var productCostElement = document.getElementById('productCost');
                                            var productPriceElement = document.getElementById('productPrice');
                                            var productDisElement = document.getElementById('productDis');

                                            var productCost = parseFloat(productCostElement.innerText.replace(/[^0-9.-]+/g, ''));   <!-- 숫자 이외의 문자를 제거 -->
                                            var productPrice = parseFloat(productPriceElement.innerText.replace(/[^0-9.-]+/g, ''));

                                            // 소비자가와 판매가가 유효한 숫자인지 확인합니다.
                                            if (!isNaN(productCost) && !isNaN(productPrice) && productCost > 0) {
                                                // 할인율 계산
                                                var discountRate = ((productCost - productPrice) / productCost) * 100;

                                                // 계산된 할인율의 정수 부분을 할인율 표시 부분에 업데이트합니다.
                                                productDisElement.innerText = Math.floor(discountRate) + '%';
                                            } else {
                                                // 유효한 값이 아닌 경우에 대한 처리 (예: 에러 메시지 출력 등)
                                                console.error("소비자가나 판매가가 유효하지 않습니다.");
                                            }
                                        </script>

                                        <tr>
                                            <th scope="row">구매 수량</th>      <!--고객이 구매하는 수량-->
                                            <td>
                                                <div class="product-quality" id="productQualityContainer">
                                                    <input id="productNum" name="productNum" class="cart-plus-minus-box input-text qty text" value="1">
                                                </div>
                                            </td>
                                        </tr>
                                        <tr class="product-delivery-fee">
                                            <th scope="row">배송비</th>
                                            <td>3,000원 (50,000원 이상 구매 시 무료)</td>
                                        </tr>
                                        <tr class="horizontal-line"></tr>
                                        <tr>
                                            <th scope="row"></th>
                                            <td style="text-align: right;">     <!--구매 총 수량 및 총 금액-->
                                                총 상품금액 (수량) : 총 <span id="producTotal" style="white-space: nowrap;"></span>&nbsp;
                                                (<span id="displayedProductNum">1</span> 개)
                                            </td>
                                        </tr>

                                        <script>
                                            document.addEventListener('DOMContentLoaded', function () {
                                                var productNum = document.getElementById("productNum");
                                                var productQualityContainer = document.getElementById("productQualityContainer");

                                                productQualityContainer.addEventListener('click', function (event) {
                                                    var target = event.target;
                                                    if (target.classList.contains('dec')) {
                                                        decreaseQuantity();
                                                    } else if (target.classList.contains('inc')) {
                                                        increaseQuantity();
                                                    }
                                                });

                                                var i = parseInt(productNum.value);
                                                var productPrice = parseFloat(document.getElementById("productPrice").textContent.replace("원", "").replace(",", ""));

                                                // 초기화 코드 추가
                                                updateQuantityAndTotal();

                                                function increaseQuantity() {
                                                    i++;
                                                    updateQuantityAndTotal();
                                                }

                                                function decreaseQuantity() {
                                                    if (i > 1) {
                                                        i--;
                                                        updateQuantityAndTotal();
                                                    }
                                                }

                                                function updateQuantityAndTotal() {
                                                    productNum.value = i;
                                                    var producTotalNum = i * productPrice;
                                                    document.querySelector("#producTotal").textContent = producTotalNum.toLocaleString() + "원";

                                                    var totalAmountValue = i * productPrice;
                                                    document.querySelector("#displayedProductNum").textContent = i;
                                                }
                                            });
                                        </script>
                                        </tbody>
                                    </table>
                                </div>

                                <div class="row">
                                    <div class="col-lg-12">
                                        <div class="product-details-purchase" data-aos="fade-up" data-aos-delay="200">

                                            <div class="btn-product-purchase">
                                                <button type="submit" class="btn btn-primary product-purchase">구매하기</button>
                                            </div>

                                            <div class="btn-product-purchase">
                                                <button type="button" class="btn btn-primary product-purchase" href="#">장바구니</button>
                                            </div>

                                            <div class="btn-product-purchase">
                                                <button type="submit" class="btn btn-primary product-purchase">카카오 구매</button>
                                                <!-- <button type="button" class="btn btn-primary product-purchase"  th:onclick="|location.href='@{/oauth2/authorization/kakao}'|">카카오 구매</button> -->
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <!-- 관심상품
                            <div class="product-details-wishlist-compare" data-aos="fade-up" data-aos-delay="300">
                                <div class="product-details-wishlist">
                                    <a href="#">
                                        <img class="injectable" src="assets/images/icon-img/heart-2.svg" alt="">
                                        Add to wishlist
                                    </a>
                                </div>
                            </div>
                            -->
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>

    <!-- 본문 시작 -->
    <div class="description-review-area pb-180 border-bottom-1 ">
        <div class="container">
            <div class="description-review-wrapper">
                <div class="tab-style-2 nav mb-70" data-aos="fade-up" data-aos-delay="200">
                    <a class="active" href="#des-details1" data-bs-toggle="tab">상품 상세 정보</a>
                    <a href="#des-details2" data-bs-toggle="tab" class="">배송안내</a>
                    <a href="#" data-bs-toggle="tab" class="">상품사용후기</a>        <!-- 배지로 수량 표시 -->
                    <a href="#" data-bs-toggle="tab">상품 Q&A</a>        <!-- 배지로 수량 표시 -->
                </div>

                <div class="tab-content">
                    <div id="des-details1" class="tab-pane active">     <!-- 상품 상세 정보 -->
                        <div class="product-description-wrapper">
                            <div class="row">
                                <div class="col-lg-1">
                                    <div class="pro-description-banner" data-aos="fade-up" data-aos-delay="300">
                                        <img src="assets/images/product-details/pro-details-banner.png" alt="">
                                    </div>
                                </div>
                                <div class="col-lg-8">
                                    <div class="pro-description-content text-center" data-aos="fade-up" data-aos-delay="400">
                                        <!-- 이미지가 있는 경우에만 표시 -->
                                        <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';">
                                    </div>
                                </div>
                                <div class="col-lg-3">
                                    <div class="pro-description-banner" data-aos="fade-up" data-aos-delay="300">
                                        <img src="assets/images/product-details/pro-details-banner.png" alt="">
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div id="des-details2" class="tab-pane">        <!-- 배송 안내 -->
                        <div class="specification-wrap table-responsive">
                            <table>
                                <tbody>
                                <tr>
                                    <td class="width1">상품결제정보</td>
                                    <td>
                                        고액결제의 경우 안전을 위해 카드사에서 확인전화를 드릴 수도 있습니다. 확인과정에서 도난 카드의 사용이나 타인 명의의 주문등
                                        정상적인 주문이 아니라고 판단될 경우 임의로 주문을 보류 또는 취소할 수 있습니다. &nbsp; <br>
                                        <br>
                                        무통장 입금은 상품 구매 대금은 PC뱅킹, 인터넷뱅킹, 텔레뱅킹 혹은 가까운 은행에서 직접 입금하시면 됩니다. &nbsp;<br>
                                        주문시 입력한&nbsp;입금자명과 실제입금자의 성명이 반드시 일치하여야 하며, 7일 이내로 입금을 하셔야 하며&nbsp;입금되지
                                        않은 주문은 자동취소 됩니다. <br>
                                    </td>
                                </tr>
                                <tr>
                                    <td class="width1">배송정보</td>
                                    <td>
                                        배송 방법 : 택배<br>
                                        배송 지역 : 전국지역<br>
                                        배송 비용 : 3,000원<br>
                                        배송 기간 : 2일 ~ 7일<br>
                                        배송 안내 :<br>
                                        배송 방법 : 택배<br>
                                        배송 지역 : 전국지역<br>
                                        배송 비용 : 조건부 무료 : 주문 금액 50,000원 미만일 때 배송비 3,000원을 추가합니다.<br>
                                        배송 기간 : 2일 ~ 7일 (생산,제조일정에 따라 달라질 수 있습니다)<br>
                                        배송 안내 : 산간벽지나 도서지방은 별도의 추가금액을 지불하셔야 합니다.<br>
                                        <br>
                                        ※고객님께서 주문하신 상품은 입금 확인후 배송해 드립니다. 다만, 상품종류에 따라서 상품의 배송이 다소 지연될 수 있습니다.<br>
                                    </td>
                                </tr>
                                <tr>
                                    <td class="width1">교환 및 반품정보</td>
                                    <td>
                                        교환 및 반품이 가능한 경우 <br>
                                        - 상품을 공급 받으신 날로부터 7일이내 단, 가전제품의 경우 포장을 개봉하였거나 포장이 훼손되어 상품가치가 상실된 경우에는 교환/반품이 불가능합니다. <br>
                                        - 공급받으신 상품 및 용역의 내용이 표시.광고 내용과 다르거나 다르게 이행된 경우에는 공급받은 날로부터 3월이내, 그사실을 알게 된 날로부터 30일이내 <br>
                                        <br>
                                        교환 및 반품이 불가능한 경우 <br>
                                        - 고객님의 책임 있는 사유로 상품등이 멸실 또는 훼손된 경우. 단, 상품의 내용을 확인하기 위하여 포장 등을 훼손한 경우는 제외 <br>
                                        - 포장을 개봉하였거나 포장이 훼손되어 상품가치가 상실된 경우 <br>
                                        (예 : 가전제품, 식품, 음반 등, 단 액정화면이 부착된 노트북, LCD모니터, 디지털 카메라 등의 불량화소에 따른 반품/교환은 제조사 기준에 따릅니다.) <br>
                                        - 고객님의 사용 또는 일부 소비에 의하여 상품의 가치가 현저히 감소한 경우 단, 화장품등의 경우 시용제품을  제공한 경우에 한 합니다. <br>
                                        - 시간의 경과에 의하여 재판매가 곤란할 정도로 상품등의 가치가 현저히 감소한 경우 <br>
                                        - 복제가 가능한 상품등의 포장을 훼손한 경우 <br>
                                        (자세한 내용은 고객만족센터 1:1 E-MAIL상담을 이용해 주시기 바랍니다.) <br>
                                        <br>
                                        ※ 고객님의 마음이 바뀌어 교환, 반품을 하실 경우 상품반송 비용은 고객님께서 부담하셔야 합니다. <br>
                                        (색상 교환, 사이즈 교환 등 포함) <br>
                                        <br>
                                    </td>
                                </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>

                    <div id="des-details3" class="tab-pane">        <!-- 상품 사용 후기 -->
                        <div class="review-wrapper">
                            <h3>1 review for Sleeve Button Cowl Neck</h3>
                            <div class="single-review">
                                <div class="review-img">
                                    <img src="assets/images/product-details/review-1.jpg" alt="">
                                </div>
                                <div class="review-content">
                                    <div class="review-rating">
                                        <a class="yellow" href="#"><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""></a>
                                        <a class="yellow" href="#"><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""></a>
                                        <a class="yellow" href="#"><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""></a>
                                        <a class="yellow" href="#"><img class="injectable" src="assets/images/icon-img/star-gray-half.svg" alt=""></a>
                                        <a class="gray" href="#"><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""></a>
                                    </div>
                                    <h5><span>HasTech</span> - April 29, 2020</h5>
                                    <p>Donec accumsan auctor iaculis. Sed suscipit arcu ligula, at egestas magna molestie a. Proin ac ex maximus, ultrices justo eget, sodales orci. Aliquam egestas libero ac turpis pharetra, in vehicula lacus scelerisque</p>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div id="des-details4" class="tab-pane">        <!-- 상품 Q&A -->
                        <div class="vendor-info-content">
                            <h3>Vendor Information</h3>
                            <ul>
                                <li><b>Store Name:</b>HasTech Fashion </li>
                                <li> <b>Vendor:</b> HasTech Fashion</li>
                                <li><b>Address:</b> PO Box 16122 Collins Street West <br>Melbourne Victoria </li>
                                <li class="rating"><span><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""><img class="injectable" src="assets/images/icon-img/star-gray-full.svg" alt=""></span>5.00 rating from 1 review</li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <!-- 연관상품 시작 -->
    <div class="related-product section-padding-lr-1 pb-100">
        <div class="container-fluid">
            <div class="section-title-4 mb-100 text-center">
                <h2>Related Products</h2>
            </div>
            <div class="related-product-active">
                <div th:each="relatedProduct : ${relatedProducts}">
                    <div th:if="${relatedProduct.categoryTypeRole == productDTO.categoryTypeRole}"
                         class="product-wrap" data-aos="fade-up" data-aos-delay="${relatedProduct.delay}">
                        <div class="product-img img-zoom mb-4">
                            <div style="box-sizing: border-box; margin: 15px; padding: 5px; text-align: center;">
                                <a th:href="@{'/product/' + ${relatedProduct.productId}}">
                                    <img class="default-img" th:src="@{/images/item/{imageName}(imageName=${relatedProduct.productImages[0]?.imageFile})}" width="600" height="300">
                                </a>
                                <div class="product-action-wrap">
                                    <button title="Quick View" data-bs-toggle="modal" data-bs-target="#exampleModal">
                                        <img class="injectable" src="assets/images/icon-img/eye.svg" alt="">
                                    </button>
                                </div>
                            </div>
                        </div>
                        <div class="product-content text-center">
                            <h3>
                                <a th:href="@{'/product/' + ${relatedProduct.productId}}" th:text="${relatedProduct.productName}"></a>
                            </h3>
                            <div class="product-price">
                                <span th:text="${relatedProduct.productPrice}"></span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <!-- 연관상품 끝 -->
</div>
</div>
</body>
</html>

 

▪️ 뒤로 이동

뒤로가기 버튼을 눌렀을 때 현재 페이지 이전에 머물렀던 곳으로 이동

                    <div class="back-to-shop" data-aos="fade-up" data-aos-delay="200">
                        <a href="#" onclick="goBack()">
                            <img class="injectable" src="assets/images/icon-img/arrow-left-6.svg" alt=""> Back to Shop
                        </a>
                    </div>
                    
                    <script>
                        function goBack() {
                            window.history.back();
                        }
                    </script>

 

 

▪️ 대표이미지

- iterStat : 반복문의 상태 정보를 나타내는 객체

                  각 이미지 항목에 대한 반복

더보기
  • iterStat.index: 0부터 시작하는 현재 반복의 인덱스 값을 나타냅니다.
  • iterStat.count: 1부터 시작하는 현재 반복의 인덱스 값을 나타냅니다.
  • iterStat.size: 전체 항목 수를 나타냅니다.
  • iterStat.current: 현재 반복의 항목을 나타냅니다.
  • iterStat.even: 현재 반복이 짝수 번째인지 여부를 나타냅니다.
  • iterStat.odd: 현재 반복이 홀수 번째인지 여부를 나타냅니다.
  • iterStat.first: 현재 반복이 첫 번째인지 여부를 나타냅니다.
  • iterStat.last: 현재 반복이 마지막인지 여부를 나타냅니다.

현재 이미지가 0번째(첫 번째) 이미지인 경우에 'active' 활성화

현재의 인덱스가 2가 아닌 경우에만 노출

                        <div class="col-lg-7">
                            <div class="product-details-tab" data-aos="fade-up" data-aos-delay="300">
                                <div class="pro-dec-big-img-slider">
                                <!-- 큰 이미지-->
                                    <div class="easyzoom-style" th:each="image, iterStat : ${productDTO.imageDTOs}">
                                        <div class="easyzoom easyzoom--overlay d-flex justify-content-center">
                                            <div th:href="@{/images/item/{imageDTOs}(imageDTOs=${image.imageFile})}">
                                                <img th:src="@{/images/item/{imageDTOs}(imageDTOs=${image.imageFile})}"
                                                     style="width: 530px; height: 530px; object-fit: cover;">
                                            </div>
                                        </div>
                                    </div>
                                </div>

                                <div class="product-dec-slider-small product-dec-small-style1"> <!-- 서브 이미지 -->
                                    <div class="product-dec-small" th:each="image, iterStat : ${productDTO.imageDTOs}"
                                         th:if="${iterStat.index != 2}"
                                         th:classappend="${iterStat.index == 0} ? 'active'"> <!--productDTO 이미지의 2번째까지만 노출-->
                                        <img th:src="@{/images/item/{filename}(filename=${image.imageFile})}">
                                    </div>
                                </div>
                            </div>
                        </div>

 

 

▪️ 할인율 계산

할인율 = (소비자가 - 판매가 / 소비자가) *100

 

parseFloat : 문자열을 부동 소수점 숫자(실수)로 변환하는 함수

                     문자열에서 숫자로 변환할 수 있는 첫 번째 부분만 추출하고, 나머지는 무시

                                        <tr class="product-consumer-price">
                                            <th scope="row">소비자가</th>
                                            <td id="productCost" th:text="${productDTO.productCost}"></td>
                                        </tr>

                                        <tr class="product-price">
                                            <th scope="row">판매가</th>
                                            <td id="productPrice" th:text="${productDTO.productPrice}"></td>
                                        </tr>

                                        <tr class="product-price">
                                            <th scope="row">할인율</th>
                                            <td id="productDis" th:text="${productDTO.productDis}"></td>
                                        </tr>

                                        <script>
                                            // 서버에서 전달된 소비자가와 판매가를 가져오기
                                            var productCostElement = document.getElementById('productCost');
                                            var productPriceElement = document.getElementById('productPrice');
                                            var productDisElement = document.getElementById('productDis');

                                            var productCost = parseFloat(productCostElement.innerText.replace(/[^0-9.-]+/g, ''));   <!-- 숫자 이외의 문자를 제거 -->
                                            var productPrice = parseFloat(productPriceElement.innerText.replace(/[^0-9.-]+/g, ''));

                                            // 소비자가와 판매가가 유효한 숫자인지 확인
                                            if (!isNaN(productCost) && !isNaN(productPrice) && productCost > 0) {
                                                // 할인율 계산
                                                var discountRate = ((productCost - productPrice) / productCost) * 100;

                                                // 계산된 할인율의 정수 부분을 할인율 표시 부분에 업데이트
                                                productDisElement.innerText = Math.floor(discountRate) + '%';
                                            } else {
                                                // 유효한 값이 아닌 경우에 대한 처리
                                                console.error("소비자가나 판매가가 유효하지 않습니다.");
                                            }
                                        </script>

 

 

▪️ 구매수량 / 합계 자동 계산

                                        <tr>
                                            <th scope="row">구매 수량</th>
                                            <td>
                                                <div class="product-quality" id="productQualityContainer">
                                                    <input id="productNum" name="productNum" class="cart-plus-minus-box input-text qty text" value="1">
                                                </div>
                                            </td>
                                        </tr>
                                        
                                        
                                        <tr>
                                            <th scope="row"></th>
                                            <td style="text-align: right;">
                                                총 상품금액 (수량) : 총 <span id="producTotal" style="white-space: nowrap;"></span>&nbsp;
                                                (<span id="displayedProductNum">1</span> 개)
                                            </td>
                                        </tr>
                                          <script>
                                            document.addEventListener('DOMContentLoaded', function () {
                                                var productNum = document.getElementById("productNum");
                                                var productQualityContainer = document.getElementById("productQualityContainer");

                                                productQualityContainer.addEventListener('click', function (event) {
                                                    var target = event.target;
                                                    if (target.classList.contains('dec')) {
                                                        decreaseQuantity();
                                                    } else if (target.classList.contains('inc')) {
                                                        increaseQuantity();
                                                    }
                                                });

                                                var i = parseInt(productNum.value);
                                                var productPrice = parseFloat(document.getElementById("productPrice").textContent.replace("원", "").replace(",", ""));

                                                // 초기화 코드 추가
                                                updateQuantityAndTotal();

                                                function increaseQuantity() {
                                                    i++;
                                                    updateQuantityAndTotal();
                                                }

                                                function decreaseQuantity() {
                                                    if (i > 1) {
                                                        i--;
                                                        updateQuantityAndTotal();
                                                    }
                                                }

                                                function updateQuantityAndTotal() {
                                                    productNum.value = i;
                                                    var producTotalNum = i * productPrice;
                                                    document.querySelector("#producTotal").textContent = producTotalNum.toLocaleString() + "원";

                                                    var totalAmountValue = i * productPrice;
                                                    document.querySelector("#displayedProductNum").textContent = i;
                                                }
                                            });
                                        </script>

 

▪️ 구매하기

상품진열 : 카테고리별로 진열 / ALL에는 모든 상품 진열 (단, Membersale 제외, SELL 상태만 재품만 진열)

<form th:action="@{/order?productId=${productId}}" method="get">

    ...

    <button type="submit" class="btn btn-primary product-purchase">구매하기</button>

</form>

 

▪️ 탭 이동

                <div class="tab-style-2 nav mb-70" data-aos="fade-up" data-aos-delay="200">
                    <a class="active" href="#des-details1" data-bs-toggle="tab">상품 상세 정보</a>
                    <a href="#des-details2" data-bs-toggle="tab" class="">배송안내</a>
                    <a href="#" data-bs-toggle="tab" class="">상품사용후기</a>
                    <a href="#" data-bs-toggle="tab">상품 Q&A</a>
                </div>

 

▪️ 상세 페이지

imageDTOs[2] 가져오기

                    <div id="des-details1" class="tab-pane active">
                        <div class="product-description-wrapper">
                            <div class="row">
                                <div class="col-lg-1">
                                    <div class="pro-description-banner" data-aos="fade-up" data-aos-delay="300">
                                        <img src="assets/images/product-details/pro-details-banner.png" alt="">
                                    </div>
                                </div>
                                <div class="col-lg-8">
                                    <div class="pro-description-content text-center" data-aos="fade-up" data-aos-delay="400">
                                        <!-- 이미지가 있는 경우에만 표시 -->
                                        <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';">
                                    </div>
                                </div>
                                <div class="col-lg-3">
                                    <div class="pro-description-banner" data-aos="fade-up" data-aos-delay="300">
                                        <img src="assets/images/product-details/pro-details-banner.png" alt="">
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

 

 

▪️ 배송 안내

                              <table>
                                <tbody>
                                <tr>
                                    <td class="width1">상품결제정보</td>
                                    <td>
                                        고액결제의 경우 안전을 위해 카드사에서 확인전화를 드릴 수도 있습니다. 확인과정에서 도난 카드의 사용이나 타인 명의의 주문등
                                        정상적인 주문이 아니라고 판단될 경우 임의로 주문을 보류 또는 취소할 수 있습니다. &nbsp; <br>
                                        <br>
                                        무통장 입금은 상품 구매 대금은 PC뱅킹, 인터넷뱅킹, 텔레뱅킹 혹은 가까운 은행에서 직접 입금하시면 됩니다. &nbsp;<br>
                                        주문시 입력한&nbsp;입금자명과 실제입금자의 성명이 반드시 일치하여야 하며, 7일 이내로 입금을 하셔야 하며&nbsp;입금되지
                                        않은 주문은 자동취소 됩니다. <br>
                                    </td>
                                </tr>
                                <tr>
                                    <td class="width1">배송정보</td>
                                    <td>
                                        배송 방법 : 택배<br>
                                        배송 지역 : 전국지역<br>
                                        배송 비용 : 3,000원<br>
                                        배송 기간 : 2일 ~ 7일<br>
                                        배송 안내 :<br>
                                        배송 방법 : 택배<br>
                                        배송 지역 : 전국지역<br>
                                        배송 비용 : 조건부 무료 : 주문 금액 50,000원 미만일 때 배송비 3,000원을 추가합니다.<br>
                                        배송 기간 : 2일 ~ 7일 (생산,제조일정에 따라 달라질 수 있습니다)<br>
                                        배송 안내 : 산간벽지나 도서지방은 별도의 추가금액을 지불하셔야 합니다.<br>
                                        <br>
                                        ※고객님께서 주문하신 상품은 입금 확인후 배송해 드립니다. 다만, 상품종류에 따라서 상품의 배송이 다소 지연될 수 있습니다.<br>
                                    </td>
                                </tr>
                                <tr>
                                    <td class="width1">교환 및 반품정보</td>
                                    <td>
                                        교환 및 반품이 가능한 경우 <br>
                                        - 상품을 공급 받으신 날로부터 7일이내 단, 가전제품의 경우 포장을 개봉하였거나 포장이 훼손되어 상품가치가 상실된 경우에는 교환/반품이 불가능합니다. <br>
                                        - 공급받으신 상품 및 용역의 내용이 표시.광고 내용과 다르거나 다르게 이행된 경우에는 공급받은 날로부터 3월이내, 그사실을 알게 된 날로부터 30일이내 <br>
                                        <br>
                                        교환 및 반품이 불가능한 경우 <br>
                                        - 고객님의 책임 있는 사유로 상품등이 멸실 또는 훼손된 경우. 단, 상품의 내용을 확인하기 위하여 포장 등을 훼손한 경우는 제외 <br>
                                        - 포장을 개봉하였거나 포장이 훼손되어 상품가치가 상실된 경우 <br>
                                        (예 : 가전제품, 식품, 음반 등, 단 액정화면이 부착된 노트북, LCD모니터, 디지털 카메라 등의 불량화소에 따른 반품/교환은 제조사 기준에 따릅니다.) <br>
                                        - 고객님의 사용 또는 일부 소비에 의하여 상품의 가치가 현저히 감소한 경우 단, 화장품등의 경우 시용제품을  제공한 경우에 한 합니다. <br>
                                        - 시간의 경과에 의하여 재판매가 곤란할 정도로 상품등의 가치가 현저히 감소한 경우 <br>
                                        - 복제가 가능한 상품등의 포장을 훼손한 경우 <br>
                                        (자세한 내용은 고객만족센터 1:1 E-MAIL상담을 이용해 주시기 바랍니다.) <br>
                                        <br>
                                        ※ 고객님의 마음이 바뀌어 교환, 반품을 하실 경우 상품반송 비용은 고객님께서 부담하셔야 합니다. <br>
                                        (색상 교환, 사이즈 교환 등 포함) <br>
                                        <br>
                                    </td>
                                </tr>
                                </tbody>
                            </table>

 

▪️ 연관상품

categoryTypeRole이 같은 상품 진열

 

<a th:href="@{'/product/' + ${relatedProduct.productId}}">

: 연관 상품의 이미지를 클릭하면 해당 상품의 상세 페이지로 이동

            <div class="related-product-active">
                <div th:each="relatedProduct : ${relatedProducts}">
                    <div th:if="${relatedProduct.categoryTypeRole == productDTO.categoryTypeRole}"
                         class="product-wrap" data-aos="fade-up" data-aos-delay="${relatedProduct.delay}">
                        <div class="product-img img-zoom mb-4">
                            <div style="box-sizing: border-box; margin: 15px; padding: 5px; text-align: center;">
                                <a th:href="@{'/product/' + ${relatedProduct.productId}}">
                                    <img class="default-img" th:src="@{/images/item/{imageName}(imageName=${relatedProduct.productImages[0]?.imageFile})}" width="600" height="300">
                                </a>
                                
                                // 빠른 뷰(Quick View)를 위한 모달을 열기 위한 버튼
                                <div class="product-action-wrap">
                                    <button title="Quick View" data-bs-toggle="modal" data-bs-target="#exampleModal">
                                        <img class="injectable" src="assets/images/icon-img/eye.svg" alt="">
                                    </button>
                                </div>
                            </div>
                        </div>
                        
                        //상품명 + 가격
                        <div class="product-content text-center">
                            <h3>
                                <a th:href="@{'/product/' + ${relatedProduct.productId}}" th:text="${relatedProduct.productName}"></a>
                            </h3>
                            <div class="product-price">
                                <span th:text="${relatedProduct.productPrice}"></span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>

 

반응형