sungro815 b0545ab5b9 관리자 상품관리 완료
웹사이트 상품리스트 상세보기 작업중
2026-02-20 18:11:03 +09:00

251 lines
14 KiB
PHP

@extends('web.layouts.subpage')
@push('styles')
<style>
/* 전체 레이아웃 50:50 비율 */
.p-detail-container { display: flex; gap: 40px; margin-bottom: 40px; align-items: flex-start; }
.p-detail-visual { flex: 5; }
.p-detail-info { flex: 5; display: flex; flex-direction: column; gap: 24px; }
/* 이미지 카드 */
.p-main-img-card { background: #fff; border: 1px solid #eee; border-radius: 16px; display: flex; align-items: center; justify-content: center; padding: 30px; aspect-ratio: 1/1; margin-bottom: 30px; }
.p-main-img-card img { width: 100%; max-width: 320px; height: auto; filter: drop-shadow(0 10px 20px rgba(0,0,0,0.05)); }
/* 탭 버튼 그룹 (부트스트랩 스타일) */
.p-tab-container { margin-top: 20px; }
.p-btn-group { display: flex; border: 1px solid #dee2e6; border-radius: 8px; overflow: hidden; margin-bottom: 20px; }
.p-tab-btn { flex: 1; padding: 12px; border: none; background: #f8f9fa; color: #495057; font-weight: 700; cursor: pointer; transition: 0.2s; border-right: 1px solid #dee2e6; }
.p-tab-btn:last-child { border-right: none; }
.p-tab-btn.is-active { background: #3182ce; color: #fff; }
.p-tab-content { padding: 20px; background: #fff; border: 1px solid #edf2f7; border-radius: 12px; min-height: 150px; }
.p-content-pane { display: none; font-size: 15px; color: #4a5568; line-height: 1.7; }
.p-content-pane.is-active { display: block; }
/* 권종 선택 (웹: 2줄) */
.p-sku-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.p-sku-btn { background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 16px; transition: all 0.2s; position: relative; cursor: pointer; text-align: left; }
.p-sku-btn.is-active { border-color: #3182ce; background: #ebf8ff; box-shadow: 0 0 0 1px #3182ce; }
.p-sku-price { font-size: 18px; font-weight: 800; color: #2d3748; }
/* 수량 및 결제 수단 */
.p-qty-wrapper { display: flex; align-items: center; justify-content: space-between; padding: 15px 0; border-top: 1px solid #edf2f7; border-bottom: 1px solid #edf2f7; }
.p-qty-ctrl { display: flex; align-items: center; border: 1px solid #cbd5e0; border-radius: 8px; overflow: hidden; background: #fff; }
.p-qty-btn { width: 44px; height: 44px; border: none; background: #f7fafc; font-size: 20px; cursor: pointer; }
.p-qty-input { width: 60px; height: 44px; border: none; text-align: center; font-weight: 700; font-size: 16px; }
.p-pay-method-grid { display: flex; gap: 8px; flex-wrap: wrap; }
.p-pay-btn { font-size: 12px; background: #fff; border: 1px solid #edf2f7; padding: 8px 14px; border-radius: 8px; color: #4a5568; cursor: pointer; }
.p-pay-btn.is-active { background: #3182ce; color: #fff; border-color: #3182ce; font-weight: 700; }
/* 요약 영역 */
.p-price-summary { margin-top: 30px; padding: 20px; background: #f8fafc; border-radius: 12px; }
.p-total-price { font-size: 28px; font-weight: 900; color: #2b6cb0; }
.p-btn-order-submit { background: #3182ce; color: #fff; width: 100%; height: 60px; border-radius: 12px; font-weight: 700; border: none; font-size: 18px; cursor: pointer; }
/* 모바일 대응 */
@media (max-width: 991px) {
.p-detail-container { flex-direction: column; }
/* 모바일 권종 한 줄 배치 */
.p-sku-grid { grid-template-columns: 1fr; }
/* 모바일 탭 영역 하단 배치용 순서 변경 */
.p-detail-visual { display: flex; flex-direction: column; width: 100%; }
.p-tab-container { order: 10; margin-top: 40px; } /* 결제정보 아래로 이동 */
.p-detail-info { width: 100%; }
}
</style>
@endpush
@section('subcontent')
<div class="p-detail-container">
{{-- 왼쪽 영역 --}}
<div class="p-detail-visual">
<div class="p-main-img-card">
<img src="{{ $product->thumb_path ?? '/assets/images/no-image.png' }}" id="productImg">
</div>
{{-- 상세 정보 그룹 --}}
<div class="p-tab-container">
<div class="p-btn-group">
<button type="button" class="p-tab-btn is-active" onclick="window.switchTab(this, 'desc')">상품설명</button>
<button type="button" class="p-tab-btn" onclick="window.switchTab(this, 'guide')">이용안내</button>
<button type="button" class="p-tab-btn" onclick="window.switchTab(this, 'warning')">주의사항</button>
</div>
<div class="p-tab-content">
<div id="pane-desc" class="p-content-pane is-active">{!! $product->description !!}</div>
<div id="pane-guide" class="p-content-pane">{!! $product->guide !!}</div>
<div id="pane-warning" class="p-content-pane">{!! $product->warning !!}</div>
</div>
</div>
</div>
{{-- 오른쪽 영역 --}}
<div class="p-detail-info">
<div class="p-info-header">
<span style="color:#3182ce; font-weight:700; font-size:14px; letter-spacing:1px;">VERIFIED RESELLER</span>
<h2>{{ $product->name }}</h2>
</div>
<div class="p-order-form">
<div class="p-sku-grid">
@foreach($skus as $sku)
<div class="p-sku-btn @if($loop->first) is-active @endif"
data-sku-id="{{ $sku->id }}"
data-price="{{ $sku->final_price }}"
onclick="window.handleSkuSelect(this)">
@if($sku->discount_value > 0)
<span class="p-sku-badge">{{ $sku->discount_type === 'PERCENT' ? $sku->discount_value.'%' : number_format($sku->discount_value).'원' }}</span>
@endif
<span style="display:block; font-size:13px; color:#a0aec0; margin-bottom:4px;">{{ $sku->name }}</span>
<div style="display:flex; align-items:baseline; gap:8px;">
<span class="p-sku-price">{{ number_format($sku->final_price) }}</span>
@if($sku->discount_value > 0)
<span style="font-size:13px; color:#cbd5e0; text-decoration:line-through;">{{ number_format($sku->face_value) }}</span>
@endif
</div>
</div>
@endforeach
</div>
<div class="p-qty-wrapper" style="margin-top:25px;">
<label class="p-option-title" style="margin-bottom:0;">구매 수량</label>
<div class="p-qty-ctrl">
<button type="button" class="p-qty-btn" onclick="window.changeQty(-1)">-</button>
<input type="text" id="pOrderQty" class="p-qty-input" value="{{ $product->min_buy_qty }}" readonly>
<button type="button" class="p-qty-btn" onclick="window.changeQty(1)">+</button>
</div>
</div>
<div style="margin-top:20px; padding:15px; background:#f7fafc; border-radius:12px;">
<span style="font-size:13px; font-weight:700; color:#4a5568; display:block; margin-bottom:10px;">결제 수단 선택</span>
<div class="p-pay-method-grid">
@foreach($payments as $pay)
<button type="button"
class="p-pay-btn @if($loop->first) is-active @endif"
data-pay-id="{{ $pay->id }}"
data-fee-rate="{{ (float)$pay->customer_fee_rate }}"
onclick="window.handlePaySelect(this)">
{{ $pay->display_name }}
</button>
@endforeach
</div>
</div>
<div class="p-price-summary">
<div class="p-summary-row" style="display:flex; justify-content:space-between; font-size:14px; color:#718096; margin-bottom:8px;">
<span>상품 합계</span>
<span><span id="pBaseTotal">0</span></span>
</div>
<div class="p-summary-row" style="display:flex; justify-content:space-between; font-size:14px; color:#718096; margin-bottom:8px;">
<span>결제 수수료 (<span id="pFeeRateDisplay">0</span>%)</span>
<span>+ <span id="pFeeAmount">0</span></span>
</div>
<div class="p-total-row" style="display:flex; justify-content:space-between; align-items:center; margin-top:10px; padding-top:10px; border-top:1px solid #e2e8f0;">
<span style="font-weight:800; color:#4a5568; font-size:16px;">최종 결제금액</span>
<div class="p-total-price"><span id="pTotalVal">0</span><small style="font-size:18px; margin-left:2px;"></small></div>
</div>
</div>
<div style="margin-top:25px;">
<button type="button" class="p-btn-order-submit" onclick="window.submitOrder()">구매하기</button>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
(function() {
const POLICY = {
minQty: parseInt("{{ $product->min_buy_qty }}") || 1,
maxQty: parseInt("{{ $product->max_buy_qty }}") || 999,
maxAmount: parseInt("{{ $product->max_buy_amount }}") || 0
};
let currentUnitPrice = 0;
let currentFeeRate = 0;
// 탭 전환 함수
window.switchTab = function(btn, type) {
document.querySelectorAll('.p-tab-btn').forEach(b => b.classList.remove('is-active'));
btn.classList.add('is-active');
document.querySelectorAll('.p-content-pane').forEach(p => p.classList.remove('is-active'));
document.getElementById('pane-' + type).classList.add('is-active');
};
window.handlePaySelect = function(el) {
document.querySelectorAll('.p-pay-btn').forEach(b => b.classList.remove('is-active'));
el.classList.add('is-active');
currentFeeRate = parseFloat(el.dataset.feeRate);
calculateTotal();
};
window.handleSkuSelect = function(el) {
document.querySelectorAll('.p-sku-btn').forEach(b => b.classList.remove('is-active'));
el.classList.add('is-active');
currentUnitPrice = parseInt(el.dataset.price);
validateAndCalculate();
};
window.changeQty = async function(delta) {
const qtyInput = document.getElementById('pOrderQty');
let nextQty = parseInt(qtyInput.value) + delta;
if (nextQty < POLICY.minQty) return;
if (POLICY.maxQty > 0 && nextQty > POLICY.maxQty) {
await showMsg('최대 구매 수량(' + POLICY.maxQty + '개)을 초과할 수 없습니다.', { type:'alert', title:'수량초과' });
return;
}
if (POLICY.maxAmount > 0 && (currentUnitPrice * nextQty) > POLICY.maxAmount) {
await showMsg('1회 최대 결제 금액 초과할 수 없습니다.', { type:'alert', title:'금액초과' });
return;
}
qtyInput.value = nextQty;
calculateTotal();
};
function validateAndCalculate() {
const qtyInput = document.getElementById('pOrderQty');
let qty = parseInt(qtyInput.value);
if (POLICY.maxAmount > 0 && (currentUnitPrice * qty) > POLICY.maxAmount) {
qty = Math.floor(POLICY.maxAmount / currentUnitPrice);
if(qty < POLICY.minQty) qty = POLICY.minQty;
qtyInput.value = qty;
}
calculateTotal();
}
function calculateTotal() {
const qty = parseInt(document.getElementById('pOrderQty').value);
const baseTotal = currentUnitPrice * qty;
const feeAmount = Math.floor(baseTotal * (currentFeeRate / 100));
document.getElementById('pBaseTotal').innerText = baseTotal.toLocaleString();
document.getElementById('pFeeRateDisplay').innerText = currentFeeRate;
document.getElementById('pFeeAmount').innerText = feeAmount.toLocaleString();
document.getElementById('pTotalVal').innerText = (baseTotal + feeAmount).toLocaleString();
}
window.submitOrder = async function() {
const activeSku = document.querySelector('.p-sku-btn.is-active');
const activePay = document.querySelector('.p-pay-btn.is-active');
const qty = document.getElementById('pOrderQty').value;
if (!activeSku) return await showMsg('권종을 선택해주세요.', { type:'alert', title:'권종선택' });
location.href = "/order/checkout?sku_id=" + activeSku.dataset.skuId + "&qty=" + qty + "&pay_id=" + activePay.dataset.payId;
};
document.addEventListener('DOMContentLoaded', function() {
const firstSku = document.querySelector('.p-sku-btn.is-active');
const firstPay = document.querySelector('.p-pay-btn.is-active');
if (firstSku) currentUnitPrice = parseInt(firstSku.dataset.price);
if (firstPay) currentFeeRate = parseFloat(firstPay.dataset.feeRate);
calculateTotal();
});
})();
</script>
@endpush