356 lines
15 KiB
HTML
356 lines
15 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;
|
|
--text-primary: #0f172a;
|
|
--primary-color: #3b82f6;
|
|
}
|
|
body {
|
|
background: var(--bg-body);
|
|
padding-bottom: 90px;
|
|
font-family: -apple-system, sans-serif;
|
|
color: #334155;
|
|
}
|
|
|
|
/* 顶部导航 Header */
|
|
.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; color: var(--text-primary); }
|
|
.btn-back { border: none; background: transparent; font-size: 20px; color: var(--text-primary); padding: 0; }
|
|
|
|
/* 状态头 */
|
|
.status-header {
|
|
background: #fff; padding: 20px; margin-bottom: 12px;
|
|
margin-top: 50px; /* 避开Header */
|
|
}
|
|
.status-title { font-size: 20px; font-weight: 800; color: var(--text-primary); margin-bottom: 4px; }
|
|
.status-desc { font-size: 13px; color: #64748b; }
|
|
|
|
/* 进度追踪 - 极简风 */
|
|
.track-list { margin-top: 20px; position: relative; }
|
|
.track-item { display: flex; margin-bottom: 24px; position: relative; }
|
|
.track-item:last-child { margin-bottom: 0; }
|
|
/* 连接线 */
|
|
.track-item:not(:last-child)::before {
|
|
content: ''; position: absolute; left: 7px; top: 20px; bottom: -20px;
|
|
width: 2px; background: #e2e8f0;
|
|
}
|
|
.track-dot {
|
|
width: 16px; height: 16px; border-radius: 50%; background: #cbd5e1;
|
|
margin-right: 16px; flex-shrink: 0; margin-top: 4px; border: 3px solid #fff; box-shadow: 0 0 0 1px #cbd5e1;
|
|
}
|
|
.track-item.active .track-dot {
|
|
background: var(--primary-color); box-shadow: 0 0 0 1px var(--primary-color);
|
|
}
|
|
.track-content { flex: 1; }
|
|
.track-status { font-size: 14px; font-weight: 600; color: var(--text-primary); }
|
|
.track-time { font-size: 12px; color: #94a3b8; margin-top: 2px; }
|
|
|
|
/* 通用卡片 */
|
|
.info-card { background: #fff; padding: 16px; margin-bottom: 12px; }
|
|
.card-title { font-size: 14px; font-weight: 700; color: var(--text-primary); margin-bottom: 12px; display: flex; align-items: center; }
|
|
.card-title i { margin-right: 6px; font-size: 16px; color: #64748b; }
|
|
|
|
/* 商品列表 */
|
|
.order-item { display: flex; margin-bottom: 16px; align-items: flex-start; }
|
|
.order-item:last-child { margin-bottom: 0; }
|
|
.item-img {
|
|
width: 60px; height: 60px; border-radius: 8px; object-fit: cover;
|
|
background: #f1f5f9; margin-right: 12px; cursor: pointer; flex-shrink: 0;
|
|
transition: transform 0.2s;
|
|
}
|
|
.item-img:hover { transform: scale(1.05); }
|
|
.item-info { flex: 1; }
|
|
.item-name { font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 4px; }
|
|
.item-spec { font-size: 12px; color: #64748b; margin-bottom: 4px; }
|
|
.item-price-row { display: flex; justify-content: space-between; align-items: baseline; }
|
|
.item-price { font-size: 14px; font-weight: 600; color: var(--text-primary); }
|
|
.item-qty { font-size: 12px; color: #94a3b8; margin-left: 8px; }
|
|
.item-subtotal { font-size: 13px; color: #64748b; text-align: right; }
|
|
|
|
/* 详情行 */
|
|
.detail-row { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 8px; }
|
|
.detail-label { color: #64748b; }
|
|
.detail-val { color: var(--text-primary); font-weight: 500; text-align: right; max-width: 70%; }
|
|
|
|
.total-row {
|
|
border-top: 1px solid #f1f5f9; margin-top: 12px; padding-top: 12px;
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
}
|
|
.total-price { font-size: 18px; font-weight: 800; color: var(--text-primary); }
|
|
|
|
/* 底部操作 */
|
|
.bottom-bar {
|
|
position: fixed; bottom: 0; left: 0; right: 0;
|
|
background: #fff; padding: 12px 16px 20px;
|
|
box-shadow: 0 -4px 12px rgba(0,0,0,0.05);
|
|
display: flex; justify-content: flex-end; gap: 12px;
|
|
z-index: 100;
|
|
}
|
|
.btn-outline {
|
|
padding: 8px 20px; border-radius: 20px; font-size: 13px; font-weight: 600;
|
|
background: #fff; border: 1px solid #cbd5e1; color: #475569;
|
|
}
|
|
.btn-primary-action {
|
|
padding: 8px 20px; border-radius: 20px; font-size: 13px; font-weight: 600;
|
|
background: #0f172a; border: 1px solid #0f172a; color: #fff;
|
|
}
|
|
|
|
/* 商品推荐模块 */
|
|
.recommend-section {
|
|
margin: 24px 16px;
|
|
padding: 20px 16px;
|
|
background: #fff;
|
|
border-radius: 16px;
|
|
}
|
|
.recommend-title {
|
|
font-size: 16px; font-weight: 700; color: var(--text-primary);
|
|
margin-bottom: 16px; display: flex; align-items: center;
|
|
}
|
|
.recommend-title i { margin-right: 8px; color: var(--primary-color); }
|
|
.recommend-grid {
|
|
display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;
|
|
}
|
|
.recommend-item {
|
|
background: #f8fafc; border-radius: 12px; overflow: hidden;
|
|
cursor: pointer; transition: transform 0.2s;
|
|
}
|
|
.recommend-item:active { transform: scale(0.98); }
|
|
.recommend-img {
|
|
width: 100%; height: 120px; object-fit: cover; background: #f1f5f9;
|
|
}
|
|
.recommend-info {
|
|
padding: 10px;
|
|
}
|
|
.recommend-name {
|
|
font-size: 13px; font-weight: 600; color: var(--text-primary);
|
|
margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
}
|
|
.recommend-price {
|
|
font-size: 14px; font-weight: 700; color: var(--text-primary);
|
|
}
|
|
</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 id="loading" class="text-center p-5 text-muted" style="margin-top: 50px;">加载中...</div>
|
|
<div id="content" style="display: none;">
|
|
<!-- 头部状态 -->
|
|
<div class="status-header">
|
|
<div class="status-title" id="orderStatusText">--</div>
|
|
<div class="status-desc">感谢您的光临</div>
|
|
|
|
<div class="track-list" id="trackList"></div>
|
|
</div>
|
|
|
|
<!-- 商品明细 -->
|
|
<div class="info-card">
|
|
<div class="card-title"><i class="bi bi-bag"></i> 商品明细</div>
|
|
<div id="itemList"></div>
|
|
<div class="total-row">
|
|
<span>合计</span>
|
|
<span class="total-price" id="totalAmount">¥0.00</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 配送信息 -->
|
|
<div class="info-card">
|
|
<div class="card-title"><i class="bi bi-geo-alt"></i> 配送信息</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">收货人</span>
|
|
<span class="detail-val" id="contactInfo">--</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">收货地址</span>
|
|
<span class="detail-val" id="addressInfo">--</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 订单信息 -->
|
|
<div class="info-card">
|
|
<div class="card-title"><i class="bi bi-file-text"></i> 订单信息</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">订单编号</span>
|
|
<span class="detail-val" id="orderNo">--</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">下单时间</span>
|
|
<span class="detail-val" id="createTime">--</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">支付方式</span>
|
|
<span class="detail-val" id="payMethod">--</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="height: 20px;"></div>
|
|
|
|
<!-- 商品推荐模块 -->
|
|
<div class="recommend-section" id="recommendSection" style="display: none;">
|
|
<div class="recommend-title">
|
|
<i class="bi bi-fire"></i>
|
|
<span>为您推荐</span>
|
|
</div>
|
|
<div class="recommend-grid" id="recommendGrid"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 底部操作栏 -->
|
|
<div class="bottom-bar" id="actionBar">
|
|
<button class="btn-outline" onclick="history.back()">返回列表</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>
|
|
const oid = new URLSearchParams(location.search).get('id');
|
|
let currentOrder = null;
|
|
|
|
async function init() {
|
|
if(!oid) return alert('参数错误');
|
|
|
|
try {
|
|
const { data } = await UIEnhancements.enhancedFetch(`/order/orders/${oid}`, { credentials: 'include' });
|
|
if(data.code === 200) {
|
|
currentOrder = data.data.order;
|
|
render(data.data);
|
|
document.getElementById('loading').style.display = 'none';
|
|
document.getElementById('content').style.display = 'block';
|
|
// 加载推荐商品
|
|
loadRecommendations();
|
|
} else {
|
|
alert(data.message);
|
|
}
|
|
} catch(e) {
|
|
console.error(e);
|
|
alert('加载失败');
|
|
}
|
|
}
|
|
|
|
// 加载推荐商品
|
|
async function loadRecommendations() {
|
|
try {
|
|
const res = await fetch('/recommend?limit=4', { credentials: 'include' });
|
|
const data = await res.json();
|
|
if (data.code === 200 && data.data && data.data.length > 0) {
|
|
renderRecommendations(data.data);
|
|
document.getElementById('recommendSection').style.display = 'block';
|
|
}
|
|
} catch(e) { console.error(e); }
|
|
}
|
|
|
|
function renderRecommendations(products) {
|
|
const container = document.getElementById('recommendGrid');
|
|
container.innerHTML = products.map(p => `
|
|
<div class="recommend-item" onclick="location.href='/product-detail.html?id=${p.id}'">
|
|
<img src="${p.image || '/static/images/default.jpg'}" class="recommend-img" alt="${p.name}">
|
|
<div class="recommend-info">
|
|
<div class="recommend-name">${p.name}</div>
|
|
<div class="recommend-price">¥${p.basePrice}</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function render(data) {
|
|
const o = data.order;
|
|
const items = data.items || [];
|
|
const tracks = data.tracks || [];
|
|
|
|
// 基础信息
|
|
document.getElementById('orderStatusText').textContent = getStatusText(o.orderStatus);
|
|
document.getElementById('totalAmount').textContent = `¥${o.totalAmount}`;
|
|
document.getElementById('contactInfo').textContent = `${o.contactPhone}`;
|
|
document.getElementById('addressInfo').textContent = o.address;
|
|
document.getElementById('orderNo').textContent = o.orderNo;
|
|
document.getElementById('createTime').textContent = new Date(o.createTime).toLocaleString();
|
|
document.getElementById('payMethod').textContent = o.paymentMethod === 'wechat' ? '微信支付' : (o.paymentMethod === 'alipay' ? '支付宝' : '未支付');
|
|
|
|
// 渲染商品
|
|
const productImages = data.productImages || {};
|
|
document.getElementById('itemList').innerHTML = items.map(i => {
|
|
let extra = '';
|
|
if(i.specName) extra += i.specName + ' ';
|
|
if(i.customSweetness) extra += `甜度: ${i.customSweetness} `;
|
|
if(i.customIce) extra += `冰度: ${i.customIce} `;
|
|
// 可以增加 Topping 展示
|
|
const productImage = productImages[i.productId] || '/static/images/default.jpg';
|
|
const subtotal = (parseFloat(i.price) * i.quantity).toFixed(2);
|
|
return `
|
|
<div class="order-item">
|
|
<img src="${productImage}" class="item-img"
|
|
onclick="location.href='/product-detail.html?id=${i.productId}'"
|
|
alt="${i.productName}">
|
|
<div class="item-info">
|
|
<div class="item-name">${i.productName}</div>
|
|
<div class="item-spec">${extra}</div>
|
|
<div class="item-price-row">
|
|
<div>
|
|
<span class="item-price">¥${i.price}</span>
|
|
<span class="item-qty">x${i.quantity}</span>
|
|
</div>
|
|
<div class="item-subtotal">小计: ¥${subtotal}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
|
|
// 渲染进度 (Tracks)
|
|
if(tracks.length > 0) {
|
|
document.getElementById('trackList').innerHTML = tracks.map((t, idx) => `
|
|
<div class="track-item ${idx === 0 ? 'active' : ''}">
|
|
<div class="track-dot"></div>
|
|
<div class="track-content">
|
|
<div class="track-status">${getStatusText(t.status)}</div>
|
|
<div class="track-time">${new Date(t.createTime).toLocaleString()}</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
} else {
|
|
document.getElementById('trackList').innerHTML = '<div class="text-muted small">暂无物流信息</div>';
|
|
}
|
|
|
|
// 底部按钮
|
|
const bar = document.getElementById('actionBar');
|
|
if(o.orderStatus === 'PENDING_PAY') {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'btn-primary-action';
|
|
btn.textContent = '立即支付';
|
|
btn.onclick = () => goToPayment(o.id);
|
|
bar.appendChild(btn);
|
|
}
|
|
}
|
|
|
|
function getStatusText(s) {
|
|
const map = { 'PENDING_PAY': '待支付', 'MAKING': '制作中', 'READY': '待取餐', 'COMPLETED': '已完成', 'CANCELLED': '已取消' };
|
|
return map[s] || s;
|
|
}
|
|
|
|
// 跳转到支付页面
|
|
function goToPayment(orderId) {
|
|
window.location.href = `/payment.html?orderId=${orderId}`;
|
|
}
|
|
|
|
init();
|
|
</script>
|
|
</body>
|
|
</html> |