366 lines
11 KiB
JavaScript
366 lines
11 KiB
JavaScript
// 订单结算页面脚本
|
||
let selectedAddressId = 0;
|
||
let subtotal = 0;
|
||
|
||
// 初始化页面
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 从页面获取初始数据
|
||
const defaultAddress = document.querySelector('input[name="address_id"]:checked');
|
||
if (defaultAddress) {
|
||
selectedAddressId = parseInt(defaultAddress.value);
|
||
}
|
||
|
||
// 获取商品总价
|
||
const subtotalElement = document.getElementById('subtotal');
|
||
if (subtotalElement) {
|
||
subtotal = parseFloat(subtotalElement.textContent.replace('¥', ''));
|
||
}
|
||
|
||
// 初始化地址卡片动画
|
||
initAddressAnimations();
|
||
});
|
||
|
||
// 初始化地址卡片动画
|
||
function initAddressAnimations() {
|
||
const addressCards = document.querySelectorAll('.address-card');
|
||
addressCards.forEach((card, index) => {
|
||
card.style.animationDelay = `${index * 0.1}s`;
|
||
});
|
||
}
|
||
|
||
// 选择地址(优化版本)
|
||
function selectAddress(addressId) {
|
||
selectedAddressId = addressId;
|
||
|
||
// 添加加载状态
|
||
const clickedCard = document.querySelector(`[data-address-id="${addressId}"]`);
|
||
if (clickedCard) {
|
||
clickedCard.classList.add('loading');
|
||
}
|
||
|
||
// 延迟执行UI更新,给用户视觉反馈
|
||
setTimeout(() => {
|
||
// 更新UI
|
||
document.querySelectorAll('.address-card').forEach(card => {
|
||
card.classList.remove('selected', 'loading');
|
||
});
|
||
|
||
const selectedCard = document.querySelector(`[data-address-id="${addressId}"]`);
|
||
if (selectedCard) {
|
||
selectedCard.classList.add('selected');
|
||
|
||
// 添加选中动画效果
|
||
selectedCard.style.animation = 'none';
|
||
selectedCard.offsetHeight; // 触发重排
|
||
selectedCard.style.animation = 'slideIn 0.4s ease';
|
||
}
|
||
|
||
// 更新单选按钮
|
||
const radioButton = document.querySelector(`input[value="${addressId}"]`);
|
||
if (radioButton) {
|
||
radioButton.checked = true;
|
||
|
||
// 触发单选按钮动画
|
||
const radioMark = radioButton.nextElementSibling;
|
||
if (radioMark) {
|
||
radioMark.style.transform = 'scale(1.1)';
|
||
setTimeout(() => {
|
||
radioMark.style.transform = 'scale(1)';
|
||
}, 200);
|
||
}
|
||
}
|
||
|
||
// 显示成功提示
|
||
showSelectionFeedback('地址选择成功');
|
||
}, 150);
|
||
}
|
||
|
||
// 显示选择反馈
|
||
function showSelectionFeedback(message) {
|
||
// 创建临时提示元素
|
||
const feedback = document.createElement('div');
|
||
feedback.className = 'position-fixed top-0 start-50 translate-middle-x mt-3';
|
||
feedback.style.zIndex = '9999';
|
||
feedback.innerHTML = `
|
||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||
<i class="bi bi-check-circle me-2"></i>
|
||
${message}
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(feedback);
|
||
|
||
// 3秒后自动移除
|
||
setTimeout(() => {
|
||
if (feedback.parentNode) {
|
||
feedback.remove();
|
||
}
|
||
}, 3000);
|
||
}
|
||
|
||
// 更新运费(优化版本)
|
||
function updateShippingFee() {
|
||
const shippingMethodElement = document.querySelector('input[name="shipping_method"]:checked');
|
||
if (!shippingMethodElement) return;
|
||
|
||
const shippingMethod = shippingMethodElement.value;
|
||
let fee = 0;
|
||
let description = '';
|
||
|
||
switch(shippingMethod) {
|
||
case 'express':
|
||
fee = 10;
|
||
description = '次日达服务';
|
||
break;
|
||
case 'same_day':
|
||
fee = 20;
|
||
description = '当日达服务';
|
||
break;
|
||
default:
|
||
fee = 0;
|
||
description = '标准配送';
|
||
}
|
||
|
||
// 添加动画效果更新价格
|
||
const shippingFeeElement = document.getElementById('shippingFee');
|
||
const totalAmountElement = document.getElementById('totalAmount');
|
||
|
||
if (shippingFeeElement && totalAmountElement) {
|
||
// 淡出效果
|
||
shippingFeeElement.style.opacity = '0.5';
|
||
totalAmountElement.style.opacity = '0.5';
|
||
|
||
setTimeout(() => {
|
||
shippingFeeElement.textContent = `¥${fee.toFixed(2)}`;
|
||
totalAmountElement.textContent = `¥${(subtotal + fee).toFixed(2)}`;
|
||
|
||
// 淡入效果
|
||
shippingFeeElement.style.opacity = '1';
|
||
totalAmountElement.style.opacity = '1';
|
||
|
||
// 显示配送方式反馈
|
||
if (fee > 0) {
|
||
showSelectionFeedback(`已选择${description} (+¥${fee})`);
|
||
} else {
|
||
showSelectionFeedback(`已选择${description} (免费)`);
|
||
}
|
||
}, 200);
|
||
}
|
||
}
|
||
|
||
// 提交订单(优化版本)
|
||
function submitOrder() {
|
||
// 验证地址选择
|
||
if (!selectedAddressId) {
|
||
showAlert('请选择收货地址', 'warning');
|
||
highlightAddressSection();
|
||
return;
|
||
}
|
||
|
||
// 获取表单数据
|
||
const shippingMethodElement = document.querySelector('input[name="shipping_method"]:checked');
|
||
const paymentMethodElement = document.querySelector('input[name="payment_method"]:checked');
|
||
const remarkElement = document.getElementById('orderRemark');
|
||
|
||
if (!shippingMethodElement) {
|
||
showAlert('请选择配送方式', 'warning');
|
||
highlightSection('shipping');
|
||
return;
|
||
}
|
||
|
||
if (!paymentMethodElement) {
|
||
showAlert('请选择支付方式', 'warning');
|
||
highlightSection('payment');
|
||
return;
|
||
}
|
||
|
||
const shippingMethod = shippingMethodElement.value;
|
||
const paymentMethod = paymentMethodElement.value;
|
||
const remark = remarkElement ? remarkElement.value : '';
|
||
|
||
// 获取选中的购物车商品ID
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const selectedItems = urlParams.getAll('items');
|
||
|
||
if (selectedItems.length === 0) {
|
||
showAlert('没有选中的商品', 'error');
|
||
return;
|
||
}
|
||
|
||
const orderData = {
|
||
selected_items: selectedItems,
|
||
address_id: selectedAddressId,
|
||
shipping_method: shippingMethod,
|
||
payment_method: paymentMethod,
|
||
remark: remark
|
||
};
|
||
|
||
// 显示加载状态
|
||
const submitBtn = document.querySelector('.btn-danger');
|
||
if (!submitBtn) {
|
||
showAlert('提交按钮未找到', 'error');
|
||
return;
|
||
}
|
||
|
||
const originalText = submitBtn.innerHTML;
|
||
submitBtn.innerHTML = '<i class="bi bi-hourglass-split"></i> 提交中...';
|
||
submitBtn.disabled = true;
|
||
submitBtn.classList.add('loading');
|
||
|
||
// 添加页面加载遮罩
|
||
showLoadingOverlay();
|
||
|
||
// 提交订单
|
||
fetch('/order/create', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(orderData)
|
||
})
|
||
.then(response => {
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
return response.json();
|
||
})
|
||
.then(data => {
|
||
hideLoadingOverlay();
|
||
|
||
if (data.success) {
|
||
showAlert('订单创建成功!正在跳转到支付页面...', 'success');
|
||
|
||
// 添加成功动画
|
||
submitBtn.innerHTML = '<i class="bi bi-check-circle"></i> 订单创建成功';
|
||
submitBtn.classList.add('btn-success');
|
||
submitBtn.classList.remove('btn-danger');
|
||
|
||
setTimeout(() => {
|
||
window.location.href = `/order/pay/${data.payment_sn}`;
|
||
}, 1500);
|
||
} else {
|
||
showAlert(data.message || '订单创建失败', 'error');
|
||
restoreSubmitButton(submitBtn, originalText);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('提交订单错误:', error);
|
||
hideLoadingOverlay();
|
||
showAlert('提交订单失败,请重试', 'error');
|
||
restoreSubmitButton(submitBtn, originalText);
|
||
});
|
||
}
|
||
|
||
// 恢复提交按钮状态
|
||
function restoreSubmitButton(submitBtn, originalText) {
|
||
submitBtn.innerHTML = originalText;
|
||
submitBtn.disabled = false;
|
||
submitBtn.classList.remove('loading', 'btn-success');
|
||
submitBtn.classList.add('btn-danger');
|
||
}
|
||
|
||
// 高亮地址选择区域
|
||
function highlightAddressSection() {
|
||
const addressSection = document.querySelector('#addressList');
|
||
if (addressSection) {
|
||
addressSection.style.border = '2px solid #dc3545';
|
||
addressSection.style.borderRadius = '8px';
|
||
addressSection.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
|
||
setTimeout(() => {
|
||
addressSection.style.border = '';
|
||
}, 3000);
|
||
}
|
||
}
|
||
|
||
// 高亮指定区域
|
||
function highlightSection(sectionType) {
|
||
let selector = '';
|
||
switch(sectionType) {
|
||
case 'shipping':
|
||
selector = 'input[name="shipping_method"]';
|
||
break;
|
||
case 'payment':
|
||
selector = 'input[name="payment_method"]';
|
||
break;
|
||
}
|
||
|
||
if (selector) {
|
||
const element = document.querySelector(selector);
|
||
if (element) {
|
||
const section = element.closest('.card');
|
||
if (section) {
|
||
section.style.border = '2px solid #dc3545';
|
||
section.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||
|
||
setTimeout(() => {
|
||
section.style.border = '';
|
||
}, 3000);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 显示加载遮罩
|
||
function showLoadingOverlay() {
|
||
const overlay = document.createElement('div');
|
||
overlay.id = 'loadingOverlay';
|
||
overlay.className = 'position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center';
|
||
overlay.style.backgroundColor = 'rgba(0,0,0,0.5)';
|
||
overlay.style.zIndex = '9999';
|
||
overlay.innerHTML = `
|
||
<div class="text-center text-white">
|
||
<div class="spinner-border mb-3" role="status">
|
||
<span class="visually-hidden">加载中...</span>
|
||
</div>
|
||
<h5>正在创建订单...</h5>
|
||
<p>请稍候,不要关闭页面</p>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(overlay);
|
||
}
|
||
|
||
// 隐藏加载遮罩
|
||
function hideLoadingOverlay() {
|
||
const overlay = document.getElementById('loadingOverlay');
|
||
if (overlay) {
|
||
overlay.remove();
|
||
}
|
||
}
|
||
|
||
// 增强的提示函数
|
||
function showAlert(message, type = 'info') {
|
||
const alertClass = {
|
||
'success': 'alert-success',
|
||
'error': 'alert-danger',
|
||
'warning': 'alert-warning',
|
||
'info': 'alert-info'
|
||
}[type] || 'alert-info';
|
||
|
||
const icon = {
|
||
'success': 'bi-check-circle',
|
||
'error': 'bi-exclamation-triangle',
|
||
'warning': 'bi-exclamation-triangle',
|
||
'info': 'bi-info-circle'
|
||
}[type] || 'bi-info-circle';
|
||
|
||
const alertDiv = document.createElement('div');
|
||
alertDiv.className = `alert ${alertClass} alert-dismissible fade show position-fixed top-0 start-50 translate-middle-x mt-3`;
|
||
alertDiv.style.zIndex = '10000';
|
||
alertDiv.innerHTML = `
|
||
<i class="bi ${icon} me-2"></i>
|
||
${message}
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
`;
|
||
|
||
document.body.appendChild(alertDiv);
|
||
|
||
// 5秒后自动移除
|
||
setTimeout(() => {
|
||
if (alertDiv.parentNode) {
|
||
alertDiv.remove();
|
||
}
|
||
}, 5000);
|
||
}
|