상세페이지 자동 연산

기여·2024년 8월 2일
0

소소한 개발팁

목록 보기
65/103

기획의도:

  • 장바구니와 마찬가지로 주문량 변경 시마다 예상결제액도 자동으로 반영되도록 함
  • 배송비도 한 번만 합산

유의할 점:

  • js가 html 요소들을 제대로 감지할 수 있도록 연산할 값들의 input 태그를 독입적으로 배치하는 게 중요 (특정 div 안에 감싸면 인식 잘안될수있음)
예:
<input type="hidden" name="pprice" th:value="${m.pprice}">	
<div th:text="'판매가: ' + ${m.fmtPprice} + '원'"></div>	
  • 해당 주문량값은 프론트에서 각 사용자의 행위에 따라 변동할 여지가 있어 db table에는 별도의 필드가 필요없고 해당 객체에만 추가하면 됨

1, Vo

@Data
public class PrdVo {
	private String product_code;    
    private String pname;
    private String pdesc;
    
    private String pimgStr; // db에는 varchar로 저장
	private MultipartFile pimg; // db엔 없고 백-프론트에만 사용
	
	private String pdate;
	
	private int pstock;
	private int pquantity = 1; //db에 아직 없음. 상세페이지 연산 위함
	
	//연산 시 int 사용, 표시는 fmt 사용
	private int pprice;
	private int pdelifee;
	
	// 통화 변환. 3자리 간 쉼표로 분리
	public String getFmtPprice() {
		NumberFormat numberFormat = 
				NumberFormat.getNumberInstance(Locale.getDefault());
		
		return numberFormat.format(pprice);
	}
	
	public String getFmtPdelifee() {
		NumberFormat numberFormat = 
				NumberFormat.getNumberInstance(Locale.getDefault());
		
		return numberFormat.format(pdelifee);
	}
}

2, Mapper

@Mapper
public interface PrdSvc {
	@Select("SELECT * FROM products "
			+ "WHERE product_code = #{product_code}")
	
		PrdVo viewPrd(PrdVo vo); // 상세보기	
}

3, Service

@Service
public class PrdSvcImpl {
	
	@Autowired
	PrdSvc prdMapper;

	public PrdVo viewPrd(PrdVo vo) {
		return prdMapper.viewPrd(vo);
	}
}

4, Ctrl

@RequestMapping("/prd/")
@Controller
public class PrdCtrl {
	
	@Autowired
	PrdSvcImpl prdSvcImpl;
    
@GetMapping("viewPrd") //상품 상세보기
	public String viewPrd(Model model, PrdVo vo) {
		System.out.println("viewPrd code: "+vo.getProduct_code());
		
		model.addAttribute("m", prdSvcImpl.viewPrd(vo));
		
		return "prd/viewPrd";
	}

5, html

<section>
    <br>
    <div align="center">
        <h1>viewPrd</h1>
        <div class="prdDetails" style="width: 900px; display: flex; flex-wrap: wrap; justify-content: left;">
            <div class="sangdan" style="width: 900px; display: flex; flex-wrap: wrap; justify-content: left;">
                <div class="sangjwa" style="width: 480px; margin: 10px; background-color: #f4f4f4; border-radius: 7px; box-shadow: 0 3px 5px #96a9fe, 0 2px 4px #96a9fe;">
                    <img th:src="@{/img/}+${m.pimgStr}" width="450" />
                </div>
                <div class="sangu" style="width: 350px; margin: 10px; padding: 10px; text-align: left;">
				    <form action="/cart/addCart" method="post">
				        <input type="hidden" name="product_code" th:value="${m.product_code}">
				        <div>
				            <strong th:text="${m.pname}"></strong>
				        </div>
				        
				        <input type="hidden" name="pprice" th:value="${m.pprice}">	
				        <div th:text="'판매가: ' + ${m.fmtPprice} + '원'"></div>				            			            
				        
				        <input type="hidden" name="pdelifee" th:value="${m.pdelifee}">
				        <div th:text="${m.pdelifee == 0 ? '무료배송❗' : '배송비: ' + m.fmtPdelifee + '원'}"></div>
				            
				        <div th:text="'주문가능 수량: ' + ${m.pstock} + '개'"></div>
				        
				        주문수량:
				        <input type="number" name="pquantity" class="quantity-input" th:value="${m.pquantity}" min="1" th:max="${m.pstock}">
				        <div class="total-price">
				            <input type="hidden" name="hab" id="habInput" th:value="${m.pprice * m.pquantity + m.pdelifee}">
				            <span id="habText"></span>
				        </div>
				        
				        <br><br>
				        
				        <div>
				            <input type="submit" value="바로 구매" class="button">
				            &emsp;
				            <input type="button" value="장바구니에 담기" class="subbtn">
				        </div>
				        
				    </form>
				</div> <!-- sangu 끝 -->
            </div>
            <div class="hadan" style="width: 900px; display: flex; flex-wrap: wrap; justify-content: left; margin: 10px; padding: 10px;">
                <div th:text="${m.pdesc}"></div>
            </div>
        </div> <!-- prdDetails 끝 -->
        <br>
    </div>
</section>

6, js

<script>
document.addEventListener('DOMContentLoaded', function() {
	
	//HTML 요소 중 입력값 선택해 변수에 저장
    const quantityInput = document.querySelector('.quantity-input');	
	const priceInput = document.querySelector('input[name="pprice"]');
    const deliveryFeeInput = document.querySelector('input[name="pdelifee"]');
    
    //위의 입력값을 숫자로 변환 후 연산 시 이용할 값의 변수명 선언
    const price = parseFloat(priceInput.value);
    const deliveryFee = parseFloat(deliveryFeeInput.value);
    
    //출력값 선언
    const habInput = document.getElementById('habInput'); //저장값
    const habText = document.getElementById('habText'); //표시값
    
    //입출력값 제대로 찍히는지 확인    
    console.log('priceInput:', priceInput);
    console.log('deliveryFeeInput:', deliveryFeeInput);
    console.log('quantityInput:', quantityInput);
    console.log('habInput:', habInput);
    console.log('habText:', habText);
    
    // 천원단위에 콤마 찍기
    function formatNumber(number) {
        return number.toLocaleString();
    }

    // 계산하기
    function updateTotal() {
        const quantity = parseInt(quantityInput.value, 10);
        const total = (price * quantity) + deliveryFee;
        habInput.value = total;
        habText.innerHTML = '예상결제액: <strong>' + formatNumber(total) + '원</strong>';
    }

    // 초기 합계 계산
    updateTotal();

    // 주문 수량이 변경될 때마다 합계 업데이트
    quantityInput.addEventListener('input', updateTotal);
});
</script>
profile
기기 좋아하는 여자

0개의 댓글