(SpringBoot) 배민 클론 코딩5 - 매장상세
깃허브 repository : SpringBoot Baemin practice
* 참고한 블로그: blog link
목차
Controller, DTO, DAO, Service, Mapper 생성
매장 상세페이지는 가게 정보 뿐만아니라 리뷰와 메뉴들도 필요하기 때문에 StoreDetail.java를 만들어준다.
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class StoreDetail {
private Store storeDetail;
// private List<Food> foodList;
// private List<Review> reviewList;
}
Controller에 getmapping 작성
@GetMapping("/store/detail/{id}")
public String storeDetail(@PathVariable long id, Model model){
StoreDetail storeDetail = storeService.storeDetail(id);
model.addAttribute("store", storeDetail);
return "store/detail";
}
필요한 service, dao 작성해준다.
public interface StoreService {
List<Store> storeList(int category, int address);
StoreDetail storeDetail(long id);
}
@Override
public StoreDetail storeDetail(long storeId) {
Store storeInfo = storeDAO.storeDetail(storeId);
// List<Food> foodList = storeDAO.foodList(storeId);
// List<Review> reviewList = storeDAO.reviewList(storeId);
return new StoreDetail(storeInfo);
}
public interface StoreDAO {
List<Store> storeList(Map<String, Object> map);
Store storeDetail(long storeId);
}
@Repository
public class StoreDAOImpl implements StoreDAO {
@Autowired
private SqlSession sql;
@Override
public List<Store> storeList(Map<String, Object> map) {
return sql.selectList("store.storeList", map);
}
@Override
public Store storeDetail(long storeId) {
return sql.selectOne("store.storeDetail", storeId);
}
}
@Select("select * from bm_store where id = #{storeId}")
public Store storeDetail(long storeId);
HTML, CSS, JS파일 추가
이제 view와 css, js설정을 해준다.
모달 설정도 들어있기 때문에 templates -> modal 경로 생성해준다.
detail.css
.fas.fa-heart {
color: red;
}
.score_box .fas {
color: gold;
}
.comment .fas {
color: gold;
}
#wrap {
width: 100%;
max-width: 1200px;
margin: 0 auto;
flex-wrap: wrap;
display: flex;
min-height: calc(100vh - 210px);
}
/* -------------- 상단 가게 정보 -------------- */
nav {
width: 100%;
text-align: center;
margin: 30px 0 30px 0;
}
nav i {
font-size: 2rem;
}
nav div {
font-size: 1.8rem;
}
nav .inf button {
width: 200px;
height: 40px;
margin-top: 20px;
font-size: 1.8rem;
background: none;
}
nav .inf .store_review_count:after {
content: '|';
}
/* -------------- 상단 가게 정보 -------------- */
/* -------------- 장바구니 -------------- */
/* 모바일 카트 */
.m_cart_img {
position: fixed;
bottom: 3%;
right: 3%;
display: none;
z-index: 11;
cursor: pointer;
}
.m_cart_img_box {
width: 70px;
height: 70px;
border-radius: 50%;
background: #2AC1BC;
box-shadow: 0px 2px 3px 0px rgb(0 0 0/ 25%);
display: flex;
justify-content: center;
align-items: center;
}
.fa-shopping-cart {
color: #fff;
font-size: 20px;
}
.m_cart_count {
width: 15px;
height: 15px;
border-radius: 50%;
position: absolute;
top: 20%;
right: 20%;
font-size: 13px;
text-align: center;
color: #2AC1BC;
background: #fff;
border: 2px solid #2AC1BC;;
vertical-align: middle;
font-weight: 1000;
display: none;
}
/* 모바일 카트 */
/* 카트 담았을때 메세지 */
.alarm {
position: fixed;
left: 50%;
top: 50%;
width: 30vw;
height: 15vh;
background: rgba(0, 0, 0, 0.7);
color: #fff;
text-align: center;
transform: translate(-50%, -50%);
border-radius: 15px;
line-height: 15vh;
z-index: 1000;
min-width: 200px;
display: none;
}
#cart {
width: 28%;
order: 1;
}
#cart .cart {
position: sticky;
top: 10%;
font-size: 1.8rem;
margin: 5%;
border: 1px solid #ddd;
}
#cart .cart h2 {
background-color: #333333;
color: #fff;
text-align: center;
}
#cart .cart .deleteAll {
font-size: 2rem;
cursor: pointer;
color: white;
position: absolute;
right: 10px;
top: 5px;
}
#cart .cart .cart_list {
max-height: 400px;
overflow: auto;
}
#cart .cart ul {
width: 90%;
margin: 0 auto;
}
#cart .cart li {
border-bottom: 1px solid #ddd;
padding: 10px 0 10px 0;
position: relative;
}
#cart .cart li .cart_menu_option {
color: #999;
}
#cart .cart button.cancle_btn {
font-size: 1.8rem;
width: 30px;
height: 30px;
position: absolute;
top: 10px;
right: 10px;
padding-bottom: 30px;
border: none;
background: none;
cursor: pointer;
}
#cart .cart button.order_btn {
width: 100%;
height: 30px;
margin-top: 20px;
font-size: 1.8rem;
border: none;
cursor: pointer;
/* background: #30DAD9; */
background: #ddd;
color: #fff;
border-radius: 0;
}
#cart .cart .total {
text-align: center;
margin-top: 10px;
}
/* -------------- 장바구니 -------------- */
/* -------------- 메인 -------------- */
main {
width: 70%;
}
main ul.tab {
display: flex;
background: #fff;
}
main ul.tab li {
width: 100%;
padding: 5px 0;
border: 1px solid #ddd;
text-align: center;
cursor: pointer;
font-size: 2rem;
}
main ul.tab li:hover {
background: #30DAD9;
color: #fff;
transition: 0.1s;
}
main ul.tab li.select {
background: #30DAD9;
color: #fff;
/* 메뉴탭 클릭시 색 바뀜 */
}
/* 메뉴 정보 리뷰 탭 */
main ul.menu {
padding-bottom: 50px;
border: 2px solid #ddd;
margin-bottom: 30px;
}
/* 메뉴 내용 */
main ul.menu li {
border-bottom: 1px solid #ddd;
}
main ul li .menu_delete_label {
margin-left: 15px;
cursor: pointer;
color: #ddd;
/* #30DAD9 */
}
main ul li .menu_delete_checkbox {
display: none;
}
main ul.menu li .menu_box {
display: flex;
justify-content: space-between;
padding: 15px;
cursor: pointer;
}
main ul.menu li .menu_box img {
width: 150px;
height: 150px;
object-fit: cover;
}
/* -------------- 메인 -------------- */
/* ----------------- 정보 탭 ----------------- */
main ul.info {
/* display: none; */
/* 카카오 지도 깨져서 스크립트로 hide함 */
}
.info li {
width: 100%;
margin: 15px auto;
}
#map_box {
position: relative;
width: 100%;
height: 400px;
margin-bottom: 10px;
}
#map_box #map {
width: 100%;
height: 100%;
}
#map_box #position_box {
position: absolute;
bottom: 10px;
right: 10px;
z-index: 1;
}
#map_box #position_box button {
border-radius: 50px;
border: 1px solid #999999;
background: #fff;
padding: 5px 10px;
font-size: 1.5rem;
}
#map_box #position_box .userPosition {
display: none;
}
.info li>div {
font-size: 1.8rem;
border-radius: 10px;
border: 1px solid #ddd;
padding: 15px;
min-height: 150px;
}
.info h2 {
padding-bottom: 10px;
}
.info div {
font-size: 1.6rem;
}
.info li .info_detail_title {
display: inline-block;
width: 200px;
}
.info li .info_detail {
display: inline-block;
}
/* ----------------- 정보 탭 ----------------- */
/* ----------------- 리뷰 탭 ----------------- */
main .comment {
display: none;
}
main .comment .score_info {
border: 1px solid #ddd;
border-radius: 10px;
padding: 15px;
display: flex;
justify-content: space-around;
}
main .comment .score_info .score {
font-size: 4.5rem;
font-weight: bold;
text-align: center;
}
main .comment .score_info i {
font-size: 1.4rem;
}
main .comment .score_count {
font-size: 1.4rem;
}
main .comment .score_count>div {
display: flex;
justify-content: space-between;
width: 150px;
}
main .comment .score_info .score_count>div div:first-child {
font-weight: bold;
}
main .comment .score_count .graph_box {
position: relative;
width: 100px;
}
main .comment .score_count .graph_box div {
width: 100%;
height: 5px;
background: #ddd;
border-radius: 5px;
position: absolute;
top: 40%;
}
main .comment .score_info .score_count .review_count {
color: #999999;
text-align: left;
width: 0px;
}
main .comment .boss_comment {
margin-bottom: 10px;
}
main .comment li {
border-radius: 10px;
width: 100%;
margin: 15px auto 30px;
font-size: 1.8rem;
}
main .comment li .nickname {
font-weight: bold;
}
main .comment li .client {
border: 1px solid #ddd;
border-radius: 10px;
padding: 15px;
}
.review_header {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
}
.review_header div div:last-child {
font-size: 1.4rem;
}
main .comment li .boss_comment_box {
background: #ddd;
width: 90%;
border-radius: 10px;
padding: 15px;
position: relative;
left: 6%;
margin-top: 15px;
}
.review_btn, .boss_comment_btn {
background: #fff;
border-radius: 5px;
padding: 5px;
}
.boss_comment .comment_area {
width: 90%;
height: 100px;
padding: 10px;
border-radius: 5px;
margin-top: 5px;
}
.boss.input {
display: none;
}
.review_img {
width: 30%;
border-radius: 10px;
cursor: pointer;
}
/* 리뷰탭 */
/* 메뉴 클릭 모달 */
.food_modal img {
width: 100%;
}
.food_modal .menu_name {
text-align: center;
display: block;
font-size: 20px;
}
.food_modal .modal_box>div {
font-size: 1.6rem;
width: 95%;
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 15px 0;
border-bottom: 1px solid #ddd;
}
.food_modal .modal_box .menu_dec {
justify-content: center;
font-size: 1.8rem;
}
.food_modal .modal_box .price {
font-weight: bold;
font-size: 2rem;
}
.food_modal .modal_box #option {
display: block;
}
.food_modal .modal_box #option .fas {
color: #ddd;
}
.food_modal .amount_box {
border: 1px solid #ddd;
display: flex;
width: 150px;
border-radius: 40px;
height: 35px;
overflow: hidden;
}
.food_modal .amount_box button {
border: none;
background: #fff;
font-size: 2.2rem;
width: 30%;
}
.food_modal .amount_box #amount {
text-align: center;
border: none;
width: 40%;
}
.food_modal .amount .amount_text {
font-size: 18px;
font-weight: bold;
line-height: 43px;
}
.food_modal .modal_box {
height: calc(100% - 130px);
}
.food_modal #btn_box {
height: 90px;
}
.food_modal #btn_box>div {
text-align: center;
height: 45px;
}
.food_modal .min_delevery {
font-size: 1.5rem;
margin-top: 5px;
}
.add_cart {
background: #30DAD9;
}
.food_modal ul {
font-size: 1.8rem;
width: 95%;
margin-left: auto;
}
.food_modal ul .option_box {
display: flex;
justify-content: space-between;
}
.food_modal ul .option_box .menu_option {
display: none;
}
/*-------메뉴리스트 클릭 모달 ---------*/
@media ( max-width :1023px) {
#wrap {
width: 95%;
}
#cart {
display: none;
}
.m_cart_img {
display: block;
}
main {
width: 100%;
}
.info li .info_detail_title {
width: 40%;
}
.store_reg_modal .modal_box ul {
margin-left: 0px;
}
.alarm {
font-size: 2rem;
}
main ul.menu li .menu_box img {
width: 125px;
height: 125px;
}
}
@media ( max-width :767px) {
html {
font-size: 60%;
}
#wrap {
width: 99%;
}
nav .inf button {
width: 100px;
font-size: 1.6rem;
border-radius: 10px;
}
main ul.tab {
position: sticky;
top: 0;
z-index: 10;
}
main ul.menu li .menu_box img {
width: 100px;
height: 100px;
}
#map_box {
height: 300px;
}
main .comment li .boss {
width: 86%;
}
.alarm {
font-size: 1.8rem;
height: 10vh;
line-height: 10vh;
}
}
modal.css
/*--------- 모달 공통 ---------*/
.modal {
width: 560px;
height: 85vh;
max-height: 720px;
position: fixed;
left: 50%;
top: 100%;
z-index: 200;
margin-left: -275px;
margin-top: 3%;
border-radius: 10px;
overflow-x: hidden;
overflow: hidden;
background: #fff;
box-shadow: 3px 3px 20px 10px rgb(0 0 0/ 25%);
}
#modal_header {
background: #333333;
position: relative;
width: 100%;
display: block;
}
.closeA {
background: #333333;
border: none;
color: #fff;
position: absolute;
cursor: pointer;
top: 0;
right: 2%;
width: 4%;
height: 100%;
font-size: 2.4rem;
}
.closeB {
background: #808080;
}
.modal h1 {
color: #fff;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
height: 40px;
}
#modal_bg {
background: rgba(0, 0, 0, 0.3);
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
display: none;
z-index: 10;
}
.modal_box {
position: relative;
overflow: auto;
overflow-x: hidden;
/* height: 70vh; */
height: calc(100% - 81px);
}
.modal_box::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera*/
}
.modal #btn_box {
/*
position: absolute;
bottom: 0;
*/
width: 100%;
font-size: 1.8rem;
border-top: 1px solid #ddd;
height: 40px;
}
.modal .btn_box input, .modal #btn_box button {
height: 40px;
font-size: 2rem;
width: 50%;
float: left;
border: none;
color: white;
border-radius: unset;
}
.modal .btn_box input[type="submit"] {
background: #30DAD9;
}
/* input file */
.modal .img_box {
width: 80%;
margin: 0 auto 50px;
}
.modal .img {
display: none;
}
.modal .img_box label {
display: inline-block;
width: 80px;
height: 60px;
border: 1px solid #ddd;
border-radius: 10px;
text-align: center;
line-height: 60px;
}
.modal .img_box div {
margin-top: 20px;
position: relative;
display: none;
width: 150px;
height: 150px;
}
.modal .img_box img {
width: 100%;
height: 100%;
border-radius: 10px;
object-fit: cover;
border: 1px solid #ddd;
}
.modal .img_close {
background: #ccc;
position: absolute;
top: -10px;
right: -10px;
width: 25px;
height: 25px;
color: #fff;
border-radius: 50%;
border: none;
}
@media ( max-width :1023px) {
}
@media ( max-width :767px) {
.modal {
width: 80%;
margin-left: -40%;
}
.closeA {
display: none;
}
.modal {
width: 100%;
margin: 0;
/* margin-top: 1vh; */
left: 0;
height: 100%;
}
.modal .btn_box input, .modal .btn_box button {
height: 45px;
}
}
@media ( max-width :480px) {
}
storeDetail.js
$(document).ready(function() {
const minDelevery = Number($("#min_delevery").data("min_delevery"));
const deleveryTip = Number($("#delevery_tip").data("delevery_tip"));
const storeId = $("#store_id").val();
const storeName = $("#store_name").data("store_name");
const cart = (function(){
// 장바구니에 담긴 가게번호 (다른가게에서 담은 상품인지 확인)
let cartStoreId = null;
const getCartStoreId = function(){
return cartStoreId;
}
const setCartStoreId = function(set){
cartStoreId = set;
}
// 장바구니에 담긴 상품 수
let cartSize = 0;
const getCartSize = function(){
return cartSize;
}
const setCartSize = function(set){
cartSize = set;
}
// 장바구니에 담은 메뉴가격 총합
let menuTotalPrice = 0;
const getMenuTotalPrice = function(){
return menuTotalPrice;
}
const setMenuTotalPrice = function(set){
menuTotalPrice = set;
}
return {
getCartStoreId : getCartStoreId,
setCartStoreId : setCartStoreId,
getCartSize : getCartSize,
setCartSize : setCartSize,
getMenuTotalPrice : getMenuTotalPrice,
setMenuTotalPrice : setMenuTotalPrice,
};
})();
// 가게 입장시 카트리스트 불러오기
/*(function(){
$.ajax({
url: "/cartList",
type: "get"
})
.done(function(result){
if(result == "" ) {
cartReset();
return;
}
cartList(result);
})
.fail(function(){
swal("장바구니 정보 에러");
})
})();*/
// 찜하기
$(".inf i").click(function(){
let likes ="";
if($(this).hasClass("far")) {
$(this).removeClass("far").addClass("fas");
likes = "on";
} else {
$(this).removeClass("fas").addClass("far");
likes = "off";
}
const data = {
id : $("#store_id").val(),
likes : likes
}
$.ajax({
url: "/store/likes",
type: "POST",
data: data
})
.done(function(result){
if(result == 0) {
} else {
let likesCount = $(".likes_count").data("count");
if(likes == "on") {
$(".likes_count").text(likesCount+1);
$(".likes_count").data("count", likesCount+1 );
} else {
$(".likes_count").text(likesCount-1);
$(".likes_count").data("count", likesCount-1 );
}
}
})
}) // 찜
// 메뉴 클릭시 모달창
$(".menu > li .menu_box").click(function() {
const isAdmin = $("#admin_button_box").data("is_admin");
if(isAdmin) {
return;
}
/*
const isOpen = $("#is_open").data("is_open");
if(!isOpen) {
swal("지금은 준비중이에요");
return;
}
*/
const foodId = $(this).find(".food_id").val();
$.ajax({
url: "/foodOption",
type: "get",
data: {foodId : foodId}
})
.done(function(result){
foodModalHtml(result);
if(result.length == 0) {
$("#option").hide();
} else {
$("#option").show();
}
})
.fail(function(){
swal("에러가 발생했습니다");
food.hide();
}) // ajax
const addCartModal = $(".food_modal");
const foodImg = $(this).find(".food_img").val();
const foodName = $(this).find(".food_name").val();
let foodPrice = Number($(this).find(".food_price").val());
const foodDec = $(this).find(".food_dec").val();
const amount = $("#amount").val();
const totalPrice = amount * foodPrice;
$(".menu_img").attr("src", foodImg);
$(".menu_name").text(foodName);
$(".menu_dec").text(foodDec);
$(".price").text(Number(foodPrice).toLocaleString() + '원');
$(".total_price").text(Number(totalPrice).toLocaleString() + '원');
$(".add_cart_food_name").val(foodName);
$(".add_cart_food_price").val(foodPrice);
$(".add_cart_food_id").val(foodId);
openModal(addCartModal);
// 수량 증가 감소
$(".minus").off().click(function() {
if (1 < Number($("#amount").val())) {
$("#amount").val(Number($("#amount").val()) - 1);
}
const amount = Number($("#amount").val());
const totalPrice = amount * foodPrice;
$(".total_price").text(Number(totalPrice).toLocaleString() + '원');
})
$(".plus").off().click(function() {
$("#amount").val(Number($("#amount").val()) + 1);
const amount = $("#amount").val();
const totalPrice = amount * foodPrice;
$(".total_price").text(Number(totalPrice).toLocaleString() + '원');
})
// 옵션 체크박스 변경
$(document).off().on("click" , ".option_box i", function(){
const optionPrice = Number($(this).siblings(".option_price").val());
if($(this).siblings(".menu_option").is(":checked")) {
$(this).siblings(".menu_option").prop("checked" ,false);
$(this).css("color" , "#ddd");
foodPrice -= optionPrice;
} else {
$(this).siblings(".menu_option").prop("checked" , true);
$(this).css("color" , "#30DAD9");
foodPrice += optionPrice;
}
const amount = Number($("#amount").val());
const totalPrice = amount * foodPrice;
$(".total_price").text(Number(totalPrice).toLocaleString() + '원');
})
}) // 메뉴 클릭
// 장바구니 담기
$(".add_cart").click(function(){
const cartStoreId = cart.getCartStoreId();
if(cartStoreId != null && storeId != cartStoreId ) {
swal({
buttons: ["취소", "담기"],
title: "장바구니에는 같은 가게의 메뉴만 담을 수 있습니다",
text: "선택하신 메뉴를 장바구니에 담을 경우 이전에 담은 메뉴가 삭제됩니다"
})
.then((value) => {
if (value == true) {
deleteCartAll();
addCart($(this));
}
});
} else {
addCart($(this));
}
}) // 장바구니 담기
// 장바구니 전부 삭제
$("#cart i").click(function(){
deleteCartAll();
})
// 리뷰탭 그래프
const reviewCount = $(".store_review_count").data("review_count");
if(reviewCount != 0) {
for(var i=1;i<=5;i++) {
const target = $(".graph.score"+i)
const score = target.data("score"+i) / reviewCount * 100;
target.css("background","gold").css("width", score+"%");
}
}
function foodModalHtml(result) {
let html = "";
for(var i=0;i<result.length;i++) {
html += `<li>
<div class="option_box">
<span>
<i class="fas fa-check-square"></i>
<input type="checkbox" class="menu_option" name="option" value="${result[i]["optionName"] }"> ${result[i]["optionName"] }
<input type="hidden" class="option_price" value="${result[i]["optionPrice"] }">
<input type="hidden" class="option_id" value="${result[i]["id"] }">
</span>
<span>${result[i]["optionPrice"] } 원</span>
</div>
</li>`;
}
$("#option ul").html(html);
}
function addCart(addCart){
// 선택한 추가옵션 배열에 저장
const foodOptionName = [];
const foodOptionPrice = [];
const foodOptionId = [];
// 선택된 추가옵션 가져오기
$("input[name='option']:checked").each(function() {
const optionName = $(this).val();
const optionId = $(this).siblings(".option_id").val();
const optionPrice = $(this).siblings(".option_price").val();
foodOptionName.push(optionName);
foodOptionId.push(optionId);
foodOptionPrice.push(optionPrice);
})
const data = {
foodId : addCart.siblings(".add_cart_food_id").val(),
foodName : addCart.siblings(".add_cart_food_name").val(),
foodPrice : addCart.siblings(".add_cart_food_price").val(),
amount : addCart.parent().siblings(".modal_box").find("#amount").val(),
optionName : foodOptionName,
optionId : foodOptionId,
optionPrice : foodOptionPrice,
deleveryTip : deleveryTip,
storeId : storeId,
storeName : storeName
}
$.ajax({
url: "/addCart",
type: "post",
data : data,
traditional : true
})
.done(function(result){
cartList(result);
alarm();
closeModal();
$("#amount").val(1);
// 밖에 있으니 작동이 안되서 추가
$(document).on("click", ".cancle_btn", function() {
const index = $(this).parent().index();
deleteCartOne(index);
}); // 장바구니 1개 삭제
}) // done
.fail(function(){
swal("에러가 발생했습니다");
}) // ajax
} // addCart
function alarm(text) {
$(".alarm").text(text);
$(".alarm").show();
setTimeout(function(){
$(".alarm").hide();
},1000);
}
// 장바구니 1개 삭제
$(document).on("click", ".cancle_btn", function() {
const index = $(this).parent().index();
deleteCartOne(index);
});
// 장바구니 1개 삭제
function deleteCartOne(index){
$.ajax({
url: "/cartOne",
type: "DELETE",
data: {index : index}
})
.done(function(result){
if(result == "") {
cartReset();
return;
}
cartList(result);
$(".m_cart_count").css("display" , "block");
$(".m_cart_count").text(result.cart.length);
})
.fail(function(){
swal("에러가 발생했습니다");
})
}
function deleteCartAll(){
$.ajax({
url: "/cartAll",
type: "DELETE"
})
.done(function(){
cartReset();
})
.fail(function(){
swal("에러가 발생했습니다");
})
}
function cartList(result){
const cartList = result.cart;
const storeId = result.storeId;
const storeName = result.storeName;
const cartTotal = result.cartTotal;
cart.setCartSize(cart.length);
let html = "";
for(var i=0;i<cartList.length;i++) {
let optionHtml = "";
if(cartList[i].optionName != null ) {
for(var j=0;j<cartList[i].optionName.length;j++) {
const optionName = cartList[i].optionName[j];
const optionPrice = Number(cartList[i].optionPrice[j]).toLocaleString();
optionHtml += `<div class="cart_menu_option"> ${optionName } ${optionPrice }원</div>`;
}
}
html += `<li>
<h3>${cartList[i].foodName }</h3>
<div>${cartList[i].foodPrice.toLocaleString()}원</div>
<div>수량 : ${cartList[i].amount }</div>
<div>${optionHtml} </div>
<div>합계 : ${cartList[i].totalPrice.toLocaleString() }원</div>
<button class="cancle_btn"> ${"x"} </button>
</li>`;
// 장바구니 추가하면 장바구니 리스트 변경
}
cart.setMenuTotalPrice(cartTotal);
cart.setCartStoreId(storeId );
$(".cart ul").html(html);
$(".total").html("총 합계 : " + cartTotal.toLocaleString() + "원");
$(".m_cart_count").css("display" , "block");
$(".m_cart_count").text(cartList.length);
console.log(cartList);
console.log("장바구니 가게 id " + storeId);
minDeleveryCheck();
}
// 주문금액이 최소주문금액 이상이어야 주문가능
function minDeleveryCheck() {
const menuTotalPrice = cart.getMenuTotalPrice();
if(minDelevery <= menuTotalPrice) {
$(".order_btn").attr("disabled", false);
$(".order_btn").css("background", "#30DAD9");
$(".order_btn").text("주문하기");
} else {
$(".order_btn").css("background", "#ddd");
$(".order_btn").attr("disabled", true);
$(".order_btn").text(minDelevery + "원 이상 주문할 수 있습니다");
}
}
// css로 display none시 카카오 맵 깨짐
$("main ul.info").hide();
// 탭 눌렀을때 색변경 콘텐츠 변경
$("ul.tab > li").click(function() {
const index = $(this).index() + 1;
$("ul.tab > li").removeClass("select");
$(this).addClass("select");
$("main ul").eq(1).hide();
$("main ul").eq(2).hide();
$("main ul").eq(3).hide();
$("main ul").eq(index).show();
const offset = $(".offset").offset();
const scrollPosition = $(document).scrollTop();
if (offset["top"] < scrollPosition) {
$("html").animate({ scrollTop: offset.top }, 100);
}
});
function cartReset() {
$(".cart ul").html("");
$(".total").html("장바구니가 비었습니다.");
$(".order_btn").css("background", "#ddd");
$(".order_btn").attr("disabled", true);
$(".order_btn").text("주문하기");
$(".m_cart_count").css("display" , "none");
$(".m_cart_count").text("");
cart.setCartSize(0);
cart.setMenuTotalPrice(0);
};
// 주문하기
$(".order_btn").click(function() {
location.href = "/order";
});
// 모바일 주문하기
$(".m_cart_img_box").click(function() {
if(cart.getCartSize() == 0 ){
alarm("메뉴를 추가해주세요");
return;
}
location.href = "/order";
});
});
nav.html
modal.html
<div id="modal_bg"></div>
<div class="food_modal modal">
<div id="modal_header">
<button type="button" class="closeA"><i class="fas fa-times"></i></button>
<h1>메뉴 상세</h1>
</div>
<div class="modal_box" >
<img src="" alt="이미지" class="menu_img" >
<h2 class="menu_name">메뉴 이름</h2>
<div class="menu_dec"></div>
<div class="price"><span>가격</span><span class="menu_price" >0</span></div>
<div id="option">
<h2>옵션 선택</h2>
<ul>
<li>
<div class="option_box">
<span>
<i class="fas fa-check-square"></i>
<input type="checkbox" class="menu_option" name="option" value="123">123
<input type="hidden" class="option_price" value="">
<input type="hidden" class="option_num" value="">
</span>
<span>0원</span>
</div>
</li>
<li>
<div class="option_box">
<span>
<i class="fas fa-check-square"></i>
<input type="checkbox" class="menu_option" name="option" value="123">123
<input type="hidden" class="option_price" value="">
<input type="hidden" class="option_id" value="">
</span>
<span>0원</span>
</div>
</li>
</ul>
</div>
<div class="amount">
<span class="amount_text">수량</span>
<span class="amount_box">
<button class="minus">-</button>
<input type="number" id="amount" min="1" value="1" readonly >
<button class="plus">+</button>
</span>
</div>
</div>
<div id="btn_box">
<input type="hidden" class="add_cart_food_name" >
<input type="hidden" class="add_cart_food_price" >
<input type="hidden" class="add_cart_food_id" >
<div>
<div class="min_delevery"><span th:text="'배달최소주문금액'+${#numbers.formatInteger(store.storeInfo.minDelevery,0,'COMMA')}+'원'"></span></div>
<div class="sum"><span>총 주문금액</span><span class="total_price">0</span></div>
</div>
<button class=closeB type="button">취소</button>
<button class="add_cart" type="button">장바구니에 담기 </button>
</div>
</div>
detail.html
<th:block th:insert="include/link.html"></th:block>
<link rel="stylesheet" href="/css/modal.css">
<link rel="stylesheet" href="/css/store/detail.css">
<th:block th:insert="include/header.html"></th:block>
<!-- 메인 -->
<th:block th:insert="store/storeDetail.html"></th:block>
<!-- 메인 -->
<!-- 푸터 -->
<th:block th:insert="include/footer.html"></th:block>
<!-- 푸터 -->
<!-- 메뉴 모달 -->
<th:block th:insert="modal/modal_food.html"></th:block>
<!-- 메뉴 모달 -->
<script type="text/javascript" src="/js/store/storeDetail.js"></script>
</body>
</html>
storeDetail.html
<div id="wrap" th:with="info=${store.storeInfo}">
<nav>
<h1 id="store_name" data-store_name="${info.storeName }" th:text="${info.storeName }"></h1>
<!-- <div id="is_open" data-is_open="${store.storeInfo.isOpen }"></div> -->
<div class="inf">
<div>
<!--
<span class="score_box">
<th:block th:each="i: ${#numbers.sequence(0, 4)}">
<i th:class="${#numbers.round(info.score) > i ? 'far fas fa-star' : 'far fa-star'}"></i>
</th:block>
<span class="store_score" data-score="${info.score}" th:text="${info.score}"></span>
</span><br>
-->
<span><i class="fas fa-heart" ></i> 찜 </span>
<span class="likes_count" data-count=0 >0</span>
</div>
<div>
<span class="store_review_count" data-review_count="0"> 리뷰 0</span>
<span>사장님 댓글 0</span>
</div>
<div id="min_delevery" data-min_delevery="${info.minDelevery }"><span th:text="'최소주문금액'+${#numbers.formatInteger(info.minDelevery,0,'COMMA')}+'원'"></span></div>
<div><span th:text="'예상 배달시간 '+${info.deleveryTime }+'분'"></span></div>
<div id="delevery_tip" data-delevery_tip="${info.deleveryTip }"><span th:text="'배달팁'+${#numbers.formatInteger(info.deleveryTip,0,'COMMA')}+'원'"></span></div>
</div>
</nav>
<!-- 모바일 카트 -->
<div class="m_cart_img">
<div class="m_cart_img_box">
<i class="fas fa-shopping-cart"></i>
<span class="m_cart_count"></span>
</div>
</div>
<!-- 모바일 카트 -->
<!-- 장바구니 -->
<aside id="cart">
<div class="cart">
<h2>장바구니</h2>
<i class="far fa-trash-alt deleteAll" ></i>
<div class="cart_list">
<ul>
<!--
<li>
<h3>메뉴</h3>
<div>가격</div>
<div>수량 : 0 </div>
<div> 옵션 </div>
<div>합계 : 0원</div>
<button class="cancle_btn"> x </button>
</li>
-->
</ul>
</div>
<div class="order_btn_box">
<div class="total">장바구니가 비었습니다.</div>
<button class="order_btn" disabled>주문하기</button>
</div>
</div>
</aside>
<div class="alarm">장바구니에 담았습니다</div>
<!-- 장바구니 -->
<main>
<div class="offset"></div>
<ul class="tab ">
<li class="select">메뉴</li>
<li>정보</li>
<li>리뷰</li>
</ul>
<!-- 메뉴 탭 -->
<ul class="menu">
</ul>
<!-- 메뉴 탭 -->
<!-- 정보 탭 -->
<ul class="info" >
</ul>
<!-- 메뉴 탭 -->
<!-- 리뷰 탭 -->
<ul class="comment" >
</ul>
</main>
</div>
<input type="hidden" value="${info.id }" id="store_id">
<input type="hidden" value="${info.category }" id="store_category">
<input type="hidden" value="${info.openingTime }" id="store_opening_time">
<input type="hidden" value="${info.closingTime }" id="store_closing_time">
<input type="hidden" value="${BMaddress.address2 }" id="delevery_address">
#numbers.formatInteger로 숫자를 형식화 해줬고 윗부분 주석부분을 보면 조건에따라 class가 바뀌어야하므로 th:class를 사용해줬다.
기존 코드는
<div id="min_delevery" data-min_delevery="${info.minDelevery }"><span th:text="'최소주문금액'+${#numbers.formatInteger(info.minDelevery,0,'COMMA')}+'원'"></span></div>
<div><span th:text="'예상 배달시간 '+${info.deleveryTime }+'분'"></span></div>
<div id="delevery_tip" data-delevery_tip="${info.deleveryTip }"><span th:text="'배달팁'+${#numbers.formatInteger(info.deleveryTip,0,'COMMA')}+'원'"></span></div>
${info.deleveryTime}인데, info값을 전달해주고있지 않아서 에러가난다.
${store.storeInfo.deleveryTime}으로 바꿔준다
<div id="min_delevery" data-min_delevery="${info.minDelevery }"><span th:text="'최소주문금액'+${#numbers.formatInteger(store.storeInfo?.minDelevery,0,'COMMA')}+'원'"></span></div>
<div><span th:text="'예상 배달시간 '+${store.storeInfo?.deleveryTime }+'분'"></span></div>
<div id="delevery_tip" data-delevery_tip="${info.deleveryTip }"><span th:text="'배달팁'+${#numbers.formatInteger(store.storeInfo?.deleveryTip,0,'COMMA')}+'원'"></span></div>
수정
내가 착각했었다. 위 방식으로 해도 되지만, 코드의 제일 윗부분을 보면 th:with를 사용해서 info변수를 정의해주고 있다. 다만 저 변수는 해당 태그 범위 내에서만 유효하기 때문에 전체를 감싸주는 첫 div태그 안에 선언해준다. 그리고 store.storeInfo부분을 다시 info로 바꿔준다.
<div id="wrap">
<nav>
<th:block th:with="info=${store.storeInfo}"></th:block>
<div id="wrap" th:with="info=${store.storeInfo}">
<nav>
<h1 id="store_name" data-store_name="${info.storeName }" th:text="${info.storeName }"></h1>