616 lines
25 KiB
HTML
616 lines
25 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 {
|
||
--primary: #0f172a;
|
||
--bg-body: #f8fafc;
|
||
--accent: #3b82f6;
|
||
}
|
||
body {
|
||
background: var(--bg-body);
|
||
padding-bottom: 120px;
|
||
font-family: -apple-system, sans-serif;
|
||
color: var(--primary);
|
||
overflow-x: hidden; /* 防止滑动动画导致横向滚动条 */
|
||
}
|
||
|
||
/* 顶部导航 */
|
||
.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);
|
||
transition: background 0.2s;
|
||
}
|
||
.section-card:active { background: #f8fafc; }
|
||
.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; }
|
||
|
||
/* 地址卡片 */
|
||
.address-card-content {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
min-height: 60px;
|
||
}
|
||
.addr-info { flex: 1; margin-right: 12px; }
|
||
.addr-top { font-size: 16px; font-weight: 700; margin-bottom: 4px; }
|
||
.addr-detail { font-size: 13px; color: #64748b; line-height: 1.4; }
|
||
.no-addr-hint { color: var(--accent); font-weight: 500; font-size: 14px; }
|
||
.icon-arrow { color: #cbd5e1; font-size: 14px; }
|
||
|
||
/* 商品列表 */
|
||
.checkout-item { display: flex; margin-bottom: 16px; }
|
||
.checkout-item:last-child { margin-bottom: 0; }
|
||
.item-img {
|
||
width: 56px; height: 56px; border-radius: 8px; background: #f1f5f9; object-fit: cover;
|
||
}
|
||
.item-content { flex: 1; margin-left: 12px; }
|
||
.item-row { display: flex; justify-content: space-between; align-items: flex-start; }
|
||
.item-name { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
|
||
.item-spec { font-size: 12px; color: #94a3b8; }
|
||
.item-price { font-size: 14px; font-weight: 600; }
|
||
.item-qty { font-size: 12px; color: #94a3b8; margin-left: 4px; }
|
||
|
||
/* 支付方式 */
|
||
.pay-option {
|
||
display: flex; align-items: center; padding: 12px 0; border-bottom: 1px solid #f1f5f9;
|
||
cursor: pointer;
|
||
}
|
||
.pay-option:last-child { border-bottom: none; }
|
||
.pay-icon { font-size: 20px; margin-right: 12px; }
|
||
.pay-wechat { color: #07c160; }
|
||
.pay-alipay { color: #1677ff; }
|
||
.pay-name { flex: 1; font-size: 14px; font-weight: 500; }
|
||
.pay-radio { accent-color: var(--primary); width: 18px; height: 18px; }
|
||
|
||
/* 备注输入 */
|
||
.remark-input {
|
||
width: 100%; border: none; background: #f8fafc; padding: 12px;
|
||
border-radius: 8px; font-size: 13px; 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; align-items: baseline; }
|
||
.total-label { font-size: 12px; color: #64748b; margin-right: 4px; }
|
||
.total-val { font-size: 24px; font-weight: 800; color: var(--primary); }
|
||
.btn-submit {
|
||
background: var(--primary); color: #fff; border: none;
|
||
padding: 14px 32px; border-radius: 12px; font-weight: 600; font-size: 16px;
|
||
}
|
||
.btn-submit:active { transform: scale(0.98); opacity: 0.9; }
|
||
.btn-submit:disabled { background: #cbd5e1; }
|
||
|
||
/* ============================
|
||
伪二级页面:地址管理
|
||
============================ */
|
||
.page-layer {
|
||
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||
background: var(--bg-body); z-index: 2000;
|
||
transform: translateX(100%); transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||
display: flex; flex-direction: column;
|
||
}
|
||
.page-layer.active { transform: translateX(0); }
|
||
|
||
.layer-body {
|
||
flex: 1; overflow-y: auto; padding: 16px; padding-bottom: 100px;
|
||
margin-top: 50px; /* Header Height */
|
||
}
|
||
|
||
/* 地址管理列表项 */
|
||
.addr-manage-item {
|
||
background: #fff; border-radius: 12px; padding: 16px; margin-bottom: 12px;
|
||
position: relative; border: 1px solid transparent;
|
||
}
|
||
.addr-manage-item.selected { border-color: var(--primary); background: #f8fafc; }
|
||
.addr-manage-top { display: flex; justify-content: space-between; margin-bottom: 6px; }
|
||
.addr-manage-name { font-weight: 700; font-size: 15px; }
|
||
.addr-manage-phone { font-weight: 400; color: #64748b; margin-left: 8px; font-size: 14px; }
|
||
.addr-manage-detail { color: #475569; font-size: 13px; line-height: 1.4; padding-right: 40px; }
|
||
.addr-tag { font-size: 10px; padding: 2px 6px; background: var(--primary); color: #fff; border-radius: 4px; vertical-align: middle; margin-right: 4px; }
|
||
|
||
/* 编辑按钮区域 */
|
||
.addr-manage-actions {
|
||
position: absolute; right: 16px; top: 50%; transform: translateY(-50%);
|
||
display: flex; flex-direction: column; gap: 12px;
|
||
padding-left: 12px; border-left: 1px solid #f1f5f9;
|
||
}
|
||
.action-icon { color: #94a3b8; font-size: 18px; padding: 4px; }
|
||
|
||
/* 底部新增按钮 */
|
||
.layer-footer {
|
||
position: fixed; bottom: 0; left: 0; right: 0;
|
||
background: #fff; padding: 12px 16px 30px;
|
||
box-shadow: 0 -4px 12px rgba(0,0,0,0.03);
|
||
}
|
||
|
||
/* 扁平化输入框 */
|
||
.flat-input {
|
||
width: 100%; padding: 12px 0; border: none; border-bottom: 1px solid #e2e8f0;
|
||
outline: none; transition: border-color 0.2s; background: transparent;
|
||
}
|
||
.flat-input:focus { border-color: var(--primary); }
|
||
|
||
/* === 修复模态框层级问题 === */
|
||
/* 确保模态框在伪二级页面(z-index: 2000)之上 */
|
||
.modal-backdrop { z-index: 2040 !important; }
|
||
.modal { z-index: 2050 !important; }
|
||
</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" onclick="openAddressManager()">
|
||
<div class="card-header"><i class="bi bi-geo-alt-fill"></i> 配送地址</div>
|
||
<div class="address-card-content">
|
||
<div class="addr-info" id="addrDisplay">
|
||
<div class="d-flex align-items-center">
|
||
<div class="spinner-border spinner-border-sm text-muted"></div>
|
||
<span class="ms-2 small text-muted">加载地址...</span>
|
||
</div>
|
||
</div>
|
||
<i class="bi bi-chevron-right icon-arrow"></i>
|
||
</div>
|
||
<input type="hidden" id="selectedAddrId">
|
||
</div>
|
||
|
||
<!-- 主页面:商品清单 -->
|
||
<div class="section-card">
|
||
<div class="card-header"><i class="bi bi-bag-fill"></i> 订单商品</div>
|
||
<div id="itemList"></div>
|
||
</div>
|
||
|
||
<!-- 支付方式选择已迁移到支付页面 -->
|
||
|
||
<!-- 主页面:备注 -->
|
||
<div class="section-card">
|
||
<div class="card-header"><i class="bi bi-chat-left-text-fill"></i> 订单备注</div>
|
||
<textarea class="remark-input" id="remark" rows="2" placeholder="如有特殊需求,请在此填写"></textarea>
|
||
</div>
|
||
|
||
<!-- 主页面:底部操作栏 -->
|
||
<div class="bottom-bar">
|
||
<div class="total-area">
|
||
<span class="total-label">合计</span>
|
||
<span class="total-val" id="totalAmount">¥0.00</span>
|
||
</div>
|
||
<button class="btn-submit" onclick="submitOrder()">提交订单</button>
|
||
</div>
|
||
|
||
<!-- ========================================== -->
|
||
<!-- 伪二级页面:地址管理 (全屏覆盖) -->
|
||
<!-- ========================================== -->
|
||
<div id="addressManagerPage" class="page-layer">
|
||
<div class="nav-header">
|
||
<button class="btn-back" onclick="closeAddressManager()"><i class="bi bi-chevron-left"></i></button>
|
||
<span class="nav-title">选择地址</span>
|
||
<div style="width: 24px;"></div>
|
||
</div>
|
||
|
||
<div class="layer-body" id="addrManagerList">
|
||
<!-- 列表动态渲染 -->
|
||
</div>
|
||
|
||
<div class="layer-footer">
|
||
<button class="btn-submit w-100" onclick="openEditModal()">+ 新增收货地址</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 地址编辑模态框 (Bootstrap Modal) -->
|
||
<div class="modal fade" id="addrEditModal" tabindex="-1">
|
||
<div class="modal-dialog modal-dialog-centered">
|
||
<div class="modal-content" style="border-radius: 16px; border:none;">
|
||
<div class="modal-header border-0">
|
||
<h5 class="modal-title fw-bold" id="modalTitle">新增地址</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="addrForm">
|
||
<input type="hidden" id="editAddrId">
|
||
<div class="mb-3">
|
||
<input type="text" class="flat-input" id="editName" placeholder="收货人姓名" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<input type="tel" class="flat-input" id="editPhone" placeholder="手机号码" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<textarea class="flat-input" id="editDetail" rows="2" placeholder="详细地址 (街道/楼牌号)" required></textarea>
|
||
</div>
|
||
<div class="form-check mt-3">
|
||
<input class="form-check-input" type="checkbox" id="editDefault">
|
||
<label class="form-check-label small text-muted" for="editDefault">设为默认地址</label>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer border-0">
|
||
<button type="button" class="btn btn-light rounded-pill" onclick="deleteCurrentAddr()" id="btnDelete" style="display:none;">删除</button>
|
||
<button type="button" class="btn btn-dark rounded-pill px-4" onclick="saveAddress()">保存并使用</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</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>
|
||
let cartItems = [];
|
||
let addresses = [];
|
||
let currentSelectedAddr = null; // 全局变量存储当前选中对象
|
||
|
||
// 初始化
|
||
(async () => {
|
||
await Promise.all([loadAddresses(), loadCart()]);
|
||
})();
|
||
|
||
// --- 核心逻辑:加载地址 ---
|
||
async function loadAddresses() {
|
||
try {
|
||
const res = await UIEnhancements.enhancedFetch('/address', { credentials: 'include' });
|
||
const json = res.data;
|
||
if(json.code === 200) {
|
||
addresses = json.data || [];
|
||
|
||
// 策略:如果没有选中过,优先选默认,否则选第一个
|
||
if (!currentSelectedAddr && addresses.length > 0) {
|
||
currentSelectedAddr = addresses.find(a => a.isDefault) || addresses[0];
|
||
}
|
||
// 如果当前选中的地址被删了,重置
|
||
else if (currentSelectedAddr && !addresses.find(a => a.id === currentSelectedAddr.id)) {
|
||
currentSelectedAddr = addresses.find(a => a.isDefault) || addresses[0] || null;
|
||
}
|
||
|
||
renderCurrentAddress();
|
||
}
|
||
} catch(e) { console.error(e); }
|
||
}
|
||
|
||
// 渲染主页面地址卡片
|
||
function renderCurrentAddress() {
|
||
const el = document.getElementById('addrDisplay');
|
||
const hidden = document.getElementById('selectedAddrId');
|
||
|
||
if (currentSelectedAddr) {
|
||
hidden.value = currentSelectedAddr.id;
|
||
// 兼容字段名:后端可能返回 contactName/recipient 或 contactPhone/phone
|
||
const name = currentSelectedAddr.contactName || currentSelectedAddr.recipient;
|
||
const phone = currentSelectedAddr.contactPhone || currentSelectedAddr.phone;
|
||
|
||
el.innerHTML = `
|
||
<div class="addr-top">
|
||
${name} <span class="fw-normal ms-2 text-muted" style="font-size:14px;">${phone}</span>
|
||
</div>
|
||
<div class="addr-detail text-truncate">${currentSelectedAddr.address}</div>
|
||
`;
|
||
} else {
|
||
hidden.value = '';
|
||
el.innerHTML = `<span class="no-addr-hint">+ 请添加收货地址</span>`;
|
||
}
|
||
}
|
||
|
||
// --- 核心逻辑:加载购物车 ---
|
||
async function loadCart() {
|
||
try {
|
||
const res = await UIEnhancements.enhancedFetch('/cart', { credentials: 'include' });
|
||
const json = res.data;
|
||
if(json.code === 200) {
|
||
cartItems = json.data || [];
|
||
if(cartItems.length === 0) {
|
||
alert('购物袋为空,请先选购');
|
||
location.href = '/index.html';
|
||
return;
|
||
}
|
||
renderItems();
|
||
calcTotal();
|
||
} else if (json.message?.includes('登录')) {
|
||
location.href = '/login.html';
|
||
}
|
||
} catch(e) { console.error(e); }
|
||
}
|
||
|
||
function renderItems() {
|
||
const container = document.getElementById('itemList');
|
||
let html = '';
|
||
cartItems.forEach(item => {
|
||
let specDesc = item.spec?.specName || '';
|
||
let price = item.product.basePrice + (item.spec?.priceAdjust || 0);
|
||
|
||
// 简单处理配料显示
|
||
let toppingTxt = '';
|
||
try {
|
||
// 实际需要请求产品详情获得配料名,此处简化
|
||
// const tIds = JSON.parse(item.toppings || '[]');
|
||
// if(tIds.length) toppingTxt = ` +配料(${tIds.length})`;
|
||
} catch(e){}
|
||
|
||
html += `
|
||
<div class="checkout-item">
|
||
<img src="${item.product.image || '/static/images/default.jpg'}" class="item-img">
|
||
<div class="item-content">
|
||
<div class="item-row">
|
||
<span class="item-name">${item.product.name}</span>
|
||
<span class="item-price">¥${price}</span>
|
||
</div>
|
||
<div class="item-row mt-1">
|
||
<span class="item-spec">${specDesc}${toppingTxt}</span>
|
||
<span class="item-qty">x${item.quantity}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
});
|
||
container.innerHTML = html;
|
||
}
|
||
|
||
async function calcTotal() {
|
||
let total = 0;
|
||
for (let item of cartItems) {
|
||
let p = item.product.basePrice + (item.spec?.priceAdjust || 0);
|
||
// 简化计算:如果需要配料价格,应在cart接口返回完整price
|
||
total += p * item.quantity;
|
||
}
|
||
document.getElementById('totalAmount').innerText = '¥' + total.toFixed(2);
|
||
}
|
||
|
||
// ==========================================
|
||
// 二级页面交互逻辑
|
||
// ==========================================
|
||
|
||
// 1. 打开地址管理页
|
||
function openAddressManager() {
|
||
const page = document.getElementById('addressManagerPage');
|
||
renderManagerList();
|
||
page.classList.add('active');
|
||
// 禁止背景滚动
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
// 2. 关闭地址管理页
|
||
function closeAddressManager() {
|
||
const page = document.getElementById('addressManagerPage');
|
||
page.classList.remove('active');
|
||
document.body.style.overflow = '';
|
||
// 关闭时刷新主页显示(可能改了选中项)
|
||
renderCurrentAddress();
|
||
}
|
||
|
||
// 3. 渲染管理列表
|
||
function renderManagerList() {
|
||
const container = document.getElementById('addrManagerList');
|
||
if (addresses.length === 0) {
|
||
container.innerHTML = '<div class="text-center py-5 text-muted">暂无地址,请添加</div>';
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = addresses.map(a => {
|
||
const name = a.contactName || a.recipient;
|
||
const phone = a.contactPhone || a.phone;
|
||
const isSelected = currentSelectedAddr && currentSelectedAddr.id === a.id;
|
||
|
||
return `
|
||
<div class="addr-manage-item ${isSelected ? 'selected' : ''}" onclick="selectAndClose('${a.id}')">
|
||
<div class="addr-manage-top">
|
||
<div class="text-truncate">
|
||
${a.isDefault ? '<span class="addr-tag">默认</span>' : ''}
|
||
<span class="addr-manage-name">${name}</span>
|
||
<span class="addr-manage-phone">${phone}</span>
|
||
</div>
|
||
</div>
|
||
<div class="addr-manage-detail">${a.address}</div>
|
||
|
||
<div class="addr-manage-actions" onclick="event.stopPropagation()">
|
||
<i class="bi bi-pencil-square action-icon" onclick="openEditModal('${a.id}')"></i>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
// 4. 在管理页选中并返回
|
||
function selectAndClose(id) {
|
||
// == 比较以兼容 string/number
|
||
const target = addresses.find(a => a.id == id);
|
||
if (target) {
|
||
currentSelectedAddr = target;
|
||
closeAddressManager();
|
||
}
|
||
}
|
||
|
||
// ==========================================
|
||
// 增删改逻辑 (修复传参问题)
|
||
// ==========================================
|
||
|
||
let editingAddrId = null;
|
||
|
||
// 打开编辑/新增模态框
|
||
function openEditModal(id = null) {
|
||
const modal = new bootstrap.Modal(document.getElementById('addrEditModal'));
|
||
const form = document.getElementById('addrForm');
|
||
const btnDel = document.getElementById('btnDelete');
|
||
|
||
form.reset();
|
||
editingAddrId = id; // 保存当前编辑ID
|
||
|
||
if (id) {
|
||
document.getElementById('modalTitle').innerText = '编辑地址';
|
||
btnDel.style.display = 'block';
|
||
// 查找对象
|
||
const target = addresses.find(a => a.id == id);
|
||
if (target) {
|
||
document.getElementById('editName').value = target.contactName || target.recipient;
|
||
document.getElementById('editPhone').value = target.contactPhone || target.phone;
|
||
document.getElementById('editDetail').value = target.address;
|
||
document.getElementById('editDefault').checked = target.isDefault;
|
||
}
|
||
} else {
|
||
document.getElementById('modalTitle').innerText = '新增地址';
|
||
btnDel.style.display = 'none';
|
||
}
|
||
|
||
modal.show();
|
||
}
|
||
|
||
// 保存地址
|
||
async function saveAddress() {
|
||
const name = document.getElementById('editName').value.trim();
|
||
const phone = document.getElementById('editPhone').value.trim();
|
||
const detail = document.getElementById('editDetail').value.trim();
|
||
const isDefault = document.getElementById('editDefault').checked;
|
||
|
||
if (!name || !phone || !detail) return alert('请填写完整信息');
|
||
|
||
// 构造符合后端规范的 Payload
|
||
// 注意:使用后端实体类字段名 contactName 和 contactPhone
|
||
const payload = {
|
||
contactName: name,
|
||
contactPhone: phone,
|
||
address: detail,
|
||
isDefault: isDefault ? 1 : 0
|
||
};
|
||
|
||
// 如果是编辑,加上 ID
|
||
// 注意:RESTful 风格通常把 ID 放在 URL 里,PUT body 里不需要 ID,但放了也无妨
|
||
|
||
try {
|
||
const url = editingAddrId ? `/address/${editingAddrId}` : '/address';
|
||
const method = editingAddrId ? 'PUT' : 'POST';
|
||
|
||
const res = await fetch(url, {
|
||
method: method,
|
||
headers: {'Content-Type': 'application/json'},
|
||
credentials: 'include',
|
||
body: JSON.stringify(payload)
|
||
});
|
||
const json = await res.json();
|
||
|
||
if (json.code === 200) {
|
||
// 成功后关闭模态框
|
||
bootstrap.Modal.getInstance(document.getElementById('addrEditModal')).hide();
|
||
// 重新加载列表
|
||
await loadAddresses();
|
||
renderManagerList(); // 刷新管理页列表
|
||
|
||
// 如果是新增的,自动选中
|
||
if (!editingAddrId) {
|
||
// 重新获取后,默认可能不是最新的,这里简单处理:不自动选中,让用户点
|
||
}
|
||
} else {
|
||
alert(json.message);
|
||
}
|
||
} catch (e) {
|
||
console.error(e);
|
||
alert('操作失败');
|
||
}
|
||
}
|
||
|
||
// 删除地址
|
||
async function deleteCurrentAddr() {
|
||
if (!editingAddrId) return;
|
||
if (!confirm('确认删除此地址?')) return;
|
||
|
||
try {
|
||
const res = await fetch(`/address/${editingAddrId}`, { method: 'DELETE' });
|
||
const json = await res.json();
|
||
if (json.code === 200) {
|
||
bootstrap.Modal.getInstance(document.getElementById('addrEditModal')).hide();
|
||
|
||
// 如果删的是当前选中的,清空选中态
|
||
if (currentSelectedAddr && currentSelectedAddr.id == editingAddrId) {
|
||
currentSelectedAddr = null;
|
||
}
|
||
|
||
await loadAddresses();
|
||
renderManagerList();
|
||
} else {
|
||
alert(json.message);
|
||
}
|
||
} catch (e) { alert('删除失败'); }
|
||
}
|
||
|
||
// ==========================================
|
||
// 提交订单(跳转到支付页面)
|
||
// ==========================================
|
||
async function submitOrder() {
|
||
if(!currentSelectedAddr) return alert('请选择收货地址');
|
||
|
||
const remark = document.getElementById('remark').value;
|
||
const btn = document.querySelector('.btn-submit');
|
||
|
||
btn.disabled = true;
|
||
btn.innerText = '提交中...';
|
||
|
||
try {
|
||
// === 提取 merchantId ===
|
||
// 从购物车第一项获取 merchantId,后端也会从购物车验证,但前端需要传参以满足验证要求
|
||
let merchantId = null;
|
||
if (cartItems.length > 0 && cartItems[0].product) {
|
||
merchantId = cartItems[0].product.merchantId;
|
||
}
|
||
|
||
if (!merchantId) {
|
||
alert('无法获取商家信息,请刷新页面重试');
|
||
btn.disabled = false;
|
||
btn.innerText = '提交订单';
|
||
return;
|
||
}
|
||
|
||
// 构造符合后端 OrderCreateRequest 的 payload
|
||
// 后端只需要:merchantId, address, contactPhone
|
||
const payload = {
|
||
merchantId: merchantId,
|
||
address: currentSelectedAddr.address,
|
||
contactPhone: currentSelectedAddr.contactPhone || currentSelectedAddr.phone
|
||
};
|
||
|
||
const res = await fetch('/order/create', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
credentials: 'include',
|
||
body: JSON.stringify(payload)
|
||
});
|
||
const json = await res.json();
|
||
|
||
if(json.code === 200) {
|
||
const orderId = json.data.id || json.data;
|
||
// 跳转到支付页面而不是订单详情页
|
||
location.href = `/payment.html?orderId=${orderId}`;
|
||
} else {
|
||
alert(json.message);
|
||
btn.disabled = false;
|
||
btn.innerText = '提交订单';
|
||
}
|
||
} catch(e) {
|
||
console.error(e);
|
||
alert('提交失败');
|
||
btn.disabled = false;
|
||
btn.innerText = '提交订单';
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |