349 lines
13 KiB
HTML
349 lines
13 KiB
HTML
|
|
<!DOCTYPE html>
|
|||
|
|
<html xmlns:th="http://www.thymeleaf.org">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>支付订单</title>
|
|||
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|||
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
|||
|
|
<style>
|
|||
|
|
:root {
|
|||
|
|
--bg-body: #f8fafc;
|
|||
|
|
--primary: #0f172a;
|
|||
|
|
--accent: #3b82f6;
|
|||
|
|
}
|
|||
|
|
body {
|
|||
|
|
background: var(--bg-body);
|
|||
|
|
padding-bottom: 120px;
|
|||
|
|
font-family: -apple-system, sans-serif;
|
|||
|
|
color: #334155;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 顶部导航 */
|
|||
|
|
.nav-header {
|
|||
|
|
position: fixed; top: 0; left: 0; right: 0; height: 50px;
|
|||
|
|
background: #fff; display: flex; align-items: center; justify-content: space-between;
|
|||
|
|
padding: 0 16px; z-index: 1000; box-shadow: 0 1px 0 rgba(0,0,0,0.05);
|
|||
|
|
}
|
|||
|
|
.nav-title { font-size: 16px; font-weight: 600; }
|
|||
|
|
.btn-back { border: none; background: transparent; font-size: 20px; padding: 0; }
|
|||
|
|
|
|||
|
|
/* 通用卡片 */
|
|||
|
|
.section-card {
|
|||
|
|
background: #fff; margin: 16px; padding: 16px; border-radius: 16px;
|
|||
|
|
box-shadow: 0 1px 2px rgba(0,0,0,0.02);
|
|||
|
|
}
|
|||
|
|
.card-header {
|
|||
|
|
font-size: 14px; font-weight: 700; margin-bottom: 12px; display: flex; align-items: center;
|
|||
|
|
}
|
|||
|
|
.card-header i { margin-right: 6px; color: #64748b; font-size: 16px; }
|
|||
|
|
|
|||
|
|
/* 订单金额 */
|
|||
|
|
.amount-display {
|
|||
|
|
text-align: center; padding: 24px 0;
|
|||
|
|
}
|
|||
|
|
.amount-label {
|
|||
|
|
font-size: 13px; color: #94a3b8; margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
.amount-value {
|
|||
|
|
font-size: 36px; font-weight: 800; color: var(--primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 商品简要信息 */
|
|||
|
|
.product-summary {
|
|||
|
|
display: flex; align-items: center; margin-bottom: 12px;
|
|||
|
|
}
|
|||
|
|
.product-summary:last-child { margin-bottom: 0; }
|
|||
|
|
.summary-img {
|
|||
|
|
width: 50px; height: 50px; border-radius: 8px; object-fit: cover;
|
|||
|
|
background: #f1f5f9; margin-right: 12px; flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
.summary-info { flex: 1; }
|
|||
|
|
.summary-name {
|
|||
|
|
font-size: 14px; font-weight: 600; color: var(--primary);
|
|||
|
|
margin-bottom: 2px;
|
|||
|
|
}
|
|||
|
|
.summary-detail {
|
|||
|
|
font-size: 12px; color: #94a3b8;
|
|||
|
|
}
|
|||
|
|
.summary-price {
|
|||
|
|
font-size: 14px; font-weight: 600; color: var(--primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 支付方式 */
|
|||
|
|
.pay-option {
|
|||
|
|
display: flex; align-items: center; padding: 16px; border-radius: 12px;
|
|||
|
|
border: 2px solid #f1f5f9; margin-bottom: 12px; cursor: pointer;
|
|||
|
|
transition: all 0.2s; background: #fff;
|
|||
|
|
}
|
|||
|
|
.pay-option:last-child { margin-bottom: 0; }
|
|||
|
|
.pay-option:hover {
|
|||
|
|
border-color: var(--accent);
|
|||
|
|
background: #f8fafc;
|
|||
|
|
}
|
|||
|
|
.pay-option.selected {
|
|||
|
|
border-color: var(--primary);
|
|||
|
|
background: #f8fafc;
|
|||
|
|
}
|
|||
|
|
.pay-icon {
|
|||
|
|
font-size: 32px; margin-right: 16px; width: 40px; text-align: center;
|
|||
|
|
}
|
|||
|
|
.pay-wechat { color: #07c160; }
|
|||
|
|
.pay-alipay { color: #1677ff; }
|
|||
|
|
.pay-info { flex: 1; }
|
|||
|
|
.pay-name {
|
|||
|
|
font-size: 15px; font-weight: 600; color: var(--primary);
|
|||
|
|
margin-bottom: 2px;
|
|||
|
|
}
|
|||
|
|
.pay-desc {
|
|||
|
|
font-size: 12px; color: #94a3b8;
|
|||
|
|
}
|
|||
|
|
.pay-radio {
|
|||
|
|
width: 20px; height: 20px; accent-color: var(--primary);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 底部操作栏 */
|
|||
|
|
.bottom-bar {
|
|||
|
|
position: fixed; bottom: 0; left: 0; right: 0;
|
|||
|
|
background: #fff; padding: 12px 20px 30px;
|
|||
|
|
box-shadow: 0 -4px 16px rgba(0,0,0,0.05);
|
|||
|
|
display: flex; justify-content: space-between; align-items: center; z-index: 100;
|
|||
|
|
}
|
|||
|
|
.total-area { display: flex; flex-direction: column; align-items: flex-end; }
|
|||
|
|
.total-label { font-size: 12px; color: #64748b; }
|
|||
|
|
.total-val { font-size: 24px; font-weight: 800; color: var(--primary); }
|
|||
|
|
.btn-pay {
|
|||
|
|
background: var(--primary); color: #fff; border: none;
|
|||
|
|
padding: 14px 40px; border-radius: 12px; font-weight: 600; font-size: 16px;
|
|||
|
|
}
|
|||
|
|
.btn-pay:active { transform: scale(0.98); opacity: 0.9; }
|
|||
|
|
.btn-pay:disabled { background: #cbd5e1; }
|
|||
|
|
</style>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<!-- 顶部导航 -->
|
|||
|
|
<div class="nav-header">
|
|||
|
|
<button class="btn-back" onclick="history.back()"><i class="bi bi-arrow-left"></i></button>
|
|||
|
|
<span class="nav-title">支付订单</span>
|
|||
|
|
<div style="width: 24px;"></div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="height: 50px;"></div>
|
|||
|
|
|
|||
|
|
<!-- 订单金额 -->
|
|||
|
|
<div class="section-card">
|
|||
|
|
<div class="amount-display">
|
|||
|
|
<div class="amount-label">需支付</div>
|
|||
|
|
<div class="amount-value" id="totalAmount">¥0.00</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 商品简要信息 -->
|
|||
|
|
<div class="section-card">
|
|||
|
|
<div class="card-header"><i class="bi bi-bag-fill"></i> 订单商品</div>
|
|||
|
|
<div id="productList"></div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 支付方式 -->
|
|||
|
|
<div class="section-card">
|
|||
|
|
<div class="card-header"><i class="bi bi-credit-card-fill"></i> 选择支付方式</div>
|
|||
|
|
<label class="pay-option" id="payWechat">
|
|||
|
|
<i class="bi bi-wechat pay-icon pay-wechat"></i>
|
|||
|
|
<div class="pay-info">
|
|||
|
|
<div class="pay-name">微信支付</div>
|
|||
|
|
<div class="pay-desc">推荐使用微信支付</div>
|
|||
|
|
</div>
|
|||
|
|
<input type="radio" name="payMethod" value="wechat" class="pay-radio" checked>
|
|||
|
|
</label>
|
|||
|
|
<label class="pay-option" id="payAlipay">
|
|||
|
|
<i class="bi bi-alipay pay-icon pay-alipay"></i>
|
|||
|
|
<div class="pay-info">
|
|||
|
|
<div class="pay-name">支付宝</div>
|
|||
|
|
<div class="pay-desc">安全便捷的支付方式</div>
|
|||
|
|
</div>
|
|||
|
|
<input type="radio" name="payMethod" value="alipay" class="pay-radio">
|
|||
|
|
</label>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 底部操作栏 -->
|
|||
|
|
<div class="bottom-bar">
|
|||
|
|
<div class="total-area">
|
|||
|
|
<span class="total-label">合计</span>
|
|||
|
|
<span class="total-val" id="bottomTotal">¥0.00</span>
|
|||
|
|
</div>
|
|||
|
|
<button class="btn-pay" onclick="submitPayment()">立即支付</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
|||
|
|
<script src="/static/js/ui-enhancements.js"></script>
|
|||
|
|
<script src="/static/js/custom-modal.js"></script>
|
|||
|
|
<script>
|
|||
|
|
let orderId = null;
|
|||
|
|
let orderData = null;
|
|||
|
|
|
|||
|
|
// 初始化
|
|||
|
|
(async () => {
|
|||
|
|
// 从URL获取订单ID
|
|||
|
|
const params = new URLSearchParams(location.search);
|
|||
|
|
orderId = params.get('orderId');
|
|||
|
|
|
|||
|
|
if (!orderId) {
|
|||
|
|
alert('订单ID不存在');
|
|||
|
|
history.back();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
await loadOrderDetail();
|
|||
|
|
} catch(e) {
|
|||
|
|
console.error('初始化失败:', e);
|
|||
|
|
alert('页面加载失败,请刷新重试');
|
|||
|
|
}
|
|||
|
|
})();
|
|||
|
|
|
|||
|
|
// 加载订单详情
|
|||
|
|
async function loadOrderDetail() {
|
|||
|
|
try {
|
|||
|
|
const res = await fetch(`/order/orders/${orderId}`, { credentials: 'include' });
|
|||
|
|
if (!res.ok) {
|
|||
|
|
throw new Error(`HTTP错误: ${res.status}`);
|
|||
|
|
}
|
|||
|
|
const data = await res.json();
|
|||
|
|
console.log('订单详情响应:', data);
|
|||
|
|
|
|||
|
|
if (data.code === 200 && data.data) {
|
|||
|
|
orderData = data.data;
|
|||
|
|
console.log('订单数据:', orderData);
|
|||
|
|
renderOrder();
|
|||
|
|
} else {
|
|||
|
|
const errorMsg = data.message || '加载订单失败';
|
|||
|
|
console.error('加载订单失败:', errorMsg, data);
|
|||
|
|
alert(errorMsg);
|
|||
|
|
history.back();
|
|||
|
|
}
|
|||
|
|
} catch(e) {
|
|||
|
|
console.error('加载订单详情失败:', e);
|
|||
|
|
alert('加载失败: ' + (e.message || '网络错误'));
|
|||
|
|
history.back();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function renderOrder() {
|
|||
|
|
if (!orderData || !orderData.order) {
|
|||
|
|
alert('订单数据异常');
|
|||
|
|
history.back();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const order = orderData.order;
|
|||
|
|
const items = orderData.items || [];
|
|||
|
|
const productImages = orderData.productImages || {};
|
|||
|
|
|
|||
|
|
// 显示金额
|
|||
|
|
const totalAmount = order.totalAmount || 0;
|
|||
|
|
document.getElementById('totalAmount').textContent = '¥' + totalAmount.toFixed(2);
|
|||
|
|
document.getElementById('bottomTotal').textContent = '¥' + totalAmount.toFixed(2);
|
|||
|
|
|
|||
|
|
// 显示商品列表(最多显示3个,超过显示"等X件商品")
|
|||
|
|
if (items.length === 0) {
|
|||
|
|
document.getElementById('productList').innerHTML = '<div class="text-center text-muted small py-2">暂无商品信息</div>';
|
|||
|
|
} else {
|
|||
|
|
const displayItems = items.slice(0, 3);
|
|||
|
|
const moreCount = items.length - 3;
|
|||
|
|
|
|||
|
|
let html = '';
|
|||
|
|
displayItems.forEach(item => {
|
|||
|
|
if (!item) return;
|
|||
|
|
const productId = item.productId || item.id;
|
|||
|
|
const image = (productImages && productImages[productId]) || '/static/images/default.jpg';
|
|||
|
|
const productName = item.productName || '商品';
|
|||
|
|
const specName = item.specName || '';
|
|||
|
|
const quantity = item.quantity || 1;
|
|||
|
|
const price = item.price || 0;
|
|||
|
|
|
|||
|
|
html += `
|
|||
|
|
<div class="product-summary">
|
|||
|
|
<img src="${image}" class="summary-img" alt="${productName}">
|
|||
|
|
<div class="summary-info">
|
|||
|
|
<div class="summary-name">${productName}</div>
|
|||
|
|
<div class="summary-detail">${specName ? specName + ' ' : ''}x${quantity}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="summary-price">¥${parseFloat(price).toFixed(2)}</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (moreCount > 0) {
|
|||
|
|
html += `<div class="text-center text-muted small mt-2">等${items.length}件商品</div>`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
document.getElementById('productList').innerHTML = html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 绑定支付方式选择
|
|||
|
|
document.querySelectorAll('input[name="payMethod"]').forEach(radio => {
|
|||
|
|
radio.addEventListener('change', function() {
|
|||
|
|
document.querySelectorAll('.pay-option').forEach(opt => {
|
|||
|
|
opt.classList.remove('selected');
|
|||
|
|
});
|
|||
|
|
if (this.value === 'wechat') {
|
|||
|
|
document.getElementById('payWechat').classList.add('selected');
|
|||
|
|
} else {
|
|||
|
|
document.getElementById('payAlipay').classList.add('selected');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 默认选中微信支付
|
|||
|
|
document.getElementById('payWechat').classList.add('selected');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交支付
|
|||
|
|
async function submitPayment() {
|
|||
|
|
const payMethod = document.querySelector('input[name="payMethod"]:checked').value;
|
|||
|
|
const btn = document.querySelector('.btn-pay');
|
|||
|
|
|
|||
|
|
// 确认支付
|
|||
|
|
const confirmed = await new Promise((resolve) => {
|
|||
|
|
if (typeof confirmAction !== 'undefined') {
|
|||
|
|
confirmAction('确认支付?', '确认支付', '确认', '取消').then(resolve);
|
|||
|
|
} else {
|
|||
|
|
resolve(confirm('确认支付?'));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
if (!confirmed) return;
|
|||
|
|
|
|||
|
|
btn.disabled = true;
|
|||
|
|
btn.textContent = '支付中...';
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const res = await fetch(`/order/${orderId}/pay`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {'Content-Type': 'application/json'},
|
|||
|
|
body: JSON.stringify({ paymentMethod: payMethod }),
|
|||
|
|
credentials: 'include'
|
|||
|
|
});
|
|||
|
|
const d = await res.json();
|
|||
|
|
|
|||
|
|
if (d.code === 200) {
|
|||
|
|
// 跳转到支付成功页面
|
|||
|
|
const order = orderData && orderData.order ? orderData.order : {};
|
|||
|
|
const orderNo = order.orderNo || '';
|
|||
|
|
const amount = order.totalAmount || 0;
|
|||
|
|
window.location.href = `/payment-success.html?orderId=${orderId}&orderNo=${orderNo}&amount=${amount}`;
|
|||
|
|
} else {
|
|||
|
|
alert(d.message || '支付失败');
|
|||
|
|
btn.disabled = false;
|
|||
|
|
btn.textContent = '立即支付';
|
|||
|
|
}
|
|||
|
|
} catch(e) {
|
|||
|
|
console.error(e);
|
|||
|
|
alert('支付失败,请重试');
|
|||
|
|
btn.disabled = false;
|
|||
|
|
btn.textContent = '立即支付';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|