1426 lines
49 KiB
JavaScript
1426 lines
49 KiB
JavaScript
// Checkout Page JavaScript - 完全重写版本
|
||
|
||
// 全局状态
|
||
const checkoutState = {
|
||
cartItems: [], // 购物车商品
|
||
addresses: [], // 用户地址列表
|
||
selectedAddress: null, // 选中的地址
|
||
coupons: [], // 可用优惠券
|
||
selectedCoupon: null, // 选中的优惠券
|
||
orderNotes: '', // 订单备注
|
||
checkoutType: 'cart', // 结算类型:'cart'(购物车) 或 'buynow'(立即购买)
|
||
paymentMethod: 'wechat', // 支付方式:'wechat'(微信) 或 'alipay'(支付宝)
|
||
paymentTimer: null, // 支付轮询定时器
|
||
countdownTimer: null, // 倒计时定时器
|
||
};
|
||
|
||
$(document).ready(function() {
|
||
initCheckoutPage();
|
||
});
|
||
|
||
function initCheckoutPage() {
|
||
const pageStartTime = Date.now();
|
||
console.log('\n=== 结算页面加载开始 ===');
|
||
|
||
// 检查结算类型
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const type = urlParams.get('type');
|
||
const productId = urlParams.get('product_id');
|
||
const skuId = urlParams.get('sku_id');
|
||
const quantity = urlParams.get('quantity');
|
||
|
||
// 绑定事件(立即绑定,不等待数据加载)
|
||
bindCheckoutEvents();
|
||
|
||
// 并行加载所有数据,提升页面加载速度
|
||
console.log('🚀 开始并行加载所有数据...');
|
||
console.log('⏱️ 请求开始时间:', new Date().toLocaleTimeString());
|
||
|
||
let dataPromise;
|
||
if (type === 'buynow' && productId) {
|
||
// 立即购买模式
|
||
checkoutState.checkoutType = 'buynow';
|
||
console.log(' [1/3] 发起商品详情 API 请求...');
|
||
dataPromise = loadBuyNowProduct(productId, skuId, quantity);
|
||
} else {
|
||
// 购物车结算模式
|
||
checkoutState.checkoutType = 'cart';
|
||
console.log(' [1/3] 发起购物车 API 请求...');
|
||
dataPromise = loadCartItems();
|
||
}
|
||
|
||
// 并行加载地址和商品数据
|
||
console.log(' [2/3] 发起地址 API 请求...');
|
||
const addressPromise = loadAddresses();
|
||
console.log(' [3/3] 所有并行请求已发起!');
|
||
console.log('⏱️ 请求发起时间:', new Date().toLocaleTimeString());
|
||
|
||
// 等待主要数据加载完成(地址和商品)
|
||
Promise.all([addressPromise, dataPromise])
|
||
.then(() => {
|
||
const pageLoadTime = Date.now() - pageStartTime;
|
||
console.log(`\n✅ 页面主要内容加载完成`);
|
||
console.log(` 总耗时: ${pageLoadTime}ms`);
|
||
console.log(` 完成时间: ${new Date().toLocaleTimeString()}`);
|
||
console.log(` 说明: 由于地址和商品并行加载,总耗时 ≈ max(地址耗时, 商品耗时)\n`);
|
||
|
||
// 异步加载优惠券(不阻塞页面显示)
|
||
// 使用 setTimeout 让页面先渲染,然后再加载优惠券
|
||
setTimeout(() => {
|
||
loadCoupons();
|
||
}, 0);
|
||
})
|
||
.catch(error => {
|
||
const pageLoadTime = Date.now() - pageStartTime;
|
||
console.error(`❌ 页面加载失败 (耗时: ${pageLoadTime}ms):`, error);
|
||
});
|
||
}
|
||
|
||
// 加载立即购买的商品
|
||
function loadBuyNowProduct(productId, skuId, quantity) {
|
||
const startTime = Date.now();
|
||
console.log(' → 商品详情 API: 请求中...');
|
||
|
||
// 显示加载状态
|
||
$('#goodsList').html(`
|
||
<div style="text-align: center; padding: 40px 20px; color: var(--text-light);">
|
||
<div class="spinner" style="margin: 0 auto 16px;"></div>
|
||
<p>加载中...</p>
|
||
</div>
|
||
`);
|
||
|
||
// 返回 Promise
|
||
return API.get(`/products/${productId}`)
|
||
.then(data => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.log(` ✓ 商品详情 API: 完成 (耗时: ${loadTime}ms)`);
|
||
console.log(' 商品:', data.data?.name || data.name || '未知');
|
||
|
||
// 支持多种数据格式
|
||
const product = data.data || data;
|
||
|
||
// 构建购买商品数据
|
||
const item = {
|
||
product_id: parseInt(productId),
|
||
product: product,
|
||
quantity: parseInt(quantity) || 1,
|
||
selected: true
|
||
};
|
||
|
||
// 如果指定了SKU
|
||
if (skuId) {
|
||
const sku = product.skus?.find(s => s.id == skuId || s.sku_id == skuId);
|
||
if (sku) {
|
||
item.sku_id = parseInt(skuId);
|
||
item.sku = sku;
|
||
}
|
||
}
|
||
|
||
checkoutState.cartItems = [item];
|
||
renderGoodsList([item]);
|
||
updatePriceSummary();
|
||
})
|
||
.catch(error => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.error(` ✗ 商品详情 API: 失败 (耗时: ${loadTime}ms)`, error.message);
|
||
Toast.error('加载商品失败');
|
||
showEmptyCart();
|
||
});
|
||
}
|
||
|
||
// 加载购物车商品
|
||
function loadCartItems() {
|
||
const startTime = Date.now();
|
||
console.log(' → 购物车 API: 请求中...');
|
||
|
||
// 显示加载状态
|
||
$('#goodsList').html(`
|
||
<div style="text-align: center; padding: 40px 20px; color: var(--text-light);">
|
||
<div class="spinner" style="margin: 0 auto 16px;"></div>
|
||
<p>加载中...</p>
|
||
</div>
|
||
`);
|
||
|
||
// 返回 Promise
|
||
return API.get('/cart')
|
||
.then(data => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.log(` ✓ 购物车 API: 完成 (耗时: ${loadTime}ms)`);
|
||
console.log(` 商品数: ${data.items?.length || 0}`);
|
||
console.log(` 总金额: ${data.total || '0'}`);
|
||
|
||
// 支持多种数据格式
|
||
let items = [];
|
||
if (Array.isArray(data)) {
|
||
// 直接返回数组
|
||
items = data;
|
||
} else if (data.data) {
|
||
// 数据在 data.data 中
|
||
if (Array.isArray(data.data)) {
|
||
items = data.data;
|
||
} else if (data.data.items) {
|
||
items = data.data.items;
|
||
} else if (data.data.list) {
|
||
items = data.data.list;
|
||
}
|
||
} else if (data.items) {
|
||
// 数据在 data.items 中
|
||
items = data.items;
|
||
} else if (data.list) {
|
||
// 数据在 data.list 中
|
||
items = data.list;
|
||
}
|
||
|
||
console.log('解析到的商品列表:', items);
|
||
|
||
checkoutState.cartItems = items;
|
||
|
||
if (items.length === 0) {
|
||
showEmptyCart();
|
||
return;
|
||
}
|
||
|
||
renderGoodsList(items);
|
||
updatePriceSummary();
|
||
})
|
||
.catch(error => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.error(` ✗ 购物车 API: 失败 (耗时: ${loadTime}ms)`, error.message);
|
||
Toast.error('加载商品失败');
|
||
showEmptyCart();
|
||
});
|
||
}
|
||
|
||
// 显示空购物车
|
||
function showEmptyCart() {
|
||
$('#goodsList').html(`
|
||
<div style="text-align: center; padding: 60px 20px; color: var(--text-light);">
|
||
<p style="font-size: 16px; margin-bottom: 20px;">购物车是空的</p>
|
||
<a href="index.html" class="btn btn-primary">
|
||
去逛逛
|
||
</a>
|
||
</div>
|
||
`);
|
||
$('#payNowBtn').prop('disabled', true);
|
||
}
|
||
|
||
// 渲染商品列表
|
||
function renderGoodsList(items) {
|
||
const goodsHtml = items.map(item => {
|
||
// 字段映射 - 支持多种格式(嵌套结构优先)
|
||
const productName = item.product_name || item.productName || item.name || item.title ||
|
||
item.goods_name ||
|
||
(item.product && (item.product.name || item.product.title)) ||
|
||
'未知商品';
|
||
|
||
// SKU 规格名称提取(支持多种格式)
|
||
let skuName = item.sku_name || item.skuName || item.specs || item.spec || '';
|
||
|
||
// 处理 spec_values 对象格式
|
||
if (!skuName && item.sku && item.sku.spec_values) {
|
||
const specValues = item.sku.spec_values;
|
||
if (typeof specValues === 'object' && specValues !== null) {
|
||
// 将对象转换为 "key:value" 格式,如 "颜色:红色 尺寸:XL"
|
||
skuName = Object.entries(specValues)
|
||
.map(([key, value]) => `${key}:${value}`)
|
||
.join(' ');
|
||
} else if (typeof specValues === 'string') {
|
||
skuName = specValues;
|
||
}
|
||
}
|
||
|
||
// 价格提取(优先从嵌套的 product 或 sku 对象中获取)
|
||
const price = parseInt(
|
||
item.price ||
|
||
item.sale_price ||
|
||
item.salePrice ||
|
||
(item.sku && item.sku.price) ||
|
||
(item.product && item.product.price) ||
|
||
0
|
||
);
|
||
|
||
const quantity = item.quantity || item.num || item.count || 1;
|
||
|
||
// 图片提取(支持嵌套的 product.main_image)
|
||
const image = item.image || item.thumb || item.main_image || item.mainImage || item.pic ||
|
||
(item.product && item.product.main_image) ||
|
||
'https://via.placeholder.com/80';
|
||
|
||
console.log('商品数据:', {
|
||
原始数据: item,
|
||
解析结果: { productName, skuName, price, quantity, image }
|
||
});
|
||
|
||
return `
|
||
<div class="goods-item">
|
||
<img src="${image}" alt="${productName}" class="goods-image">
|
||
<div class="goods-info">
|
||
<div class="goods-title">${productName}</div>
|
||
${skuName ? `<div class="goods-specs">${skuName}</div>` : ''}
|
||
</div>
|
||
<div class="goods-right">
|
||
<div class="goods-price">¥${PriceUtils.fenToYuan(price).toFixed(2)}</div>
|
||
<div class="goods-quantity">x${quantity}</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
$('#goodsList').html(goodsHtml);
|
||
}
|
||
|
||
// 加载用户地址
|
||
function loadAddresses() {
|
||
const startTime = Date.now();
|
||
console.log(' → 地址 API: 请求中...');
|
||
|
||
// 返回 Promise
|
||
return API.get('/addresses')
|
||
.then(data => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.log(` ✓ 地址 API: 完成 (耗时: ${loadTime}ms)`);
|
||
|
||
// 支持多种数据格式
|
||
let addresses = [];
|
||
if (Array.isArray(data)) {
|
||
// 直接返回数组
|
||
addresses = data;
|
||
} else if (data.data) {
|
||
// 数据在 data.data 中
|
||
addresses = Array.isArray(data.data) ? data.data : (data.data.list || []);
|
||
} else if (data.list) {
|
||
// 数据在 data.list 中
|
||
addresses = data.list;
|
||
}
|
||
|
||
console.log(' 地址数: ' + addresses.length);
|
||
|
||
checkoutState.addresses = addresses;
|
||
|
||
if (addresses.length > 0) {
|
||
// 查找默认地址
|
||
const defaultAddress = addresses.find(addr => addr.is_default || addr.isDefault);
|
||
checkoutState.selectedAddress = defaultAddress || addresses[0];
|
||
|
||
console.log(' 选中地址:', checkoutState.selectedAddress.name || '无名称');
|
||
|
||
renderSelectedAddress(checkoutState.selectedAddress);
|
||
} else {
|
||
console.log(' 没有可用地址');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.error(` ✗ 地址 API: 失败 (耗时: ${loadTime}ms)`, error.message);
|
||
// 非登录用户或没有地址时不显示错误
|
||
});
|
||
}
|
||
|
||
// 渲染选中的地址
|
||
function renderSelectedAddress(address) {
|
||
if (!address) {
|
||
$('#noAddress').show();
|
||
$('#addressInfo').hide();
|
||
return;
|
||
}
|
||
|
||
$('#noAddress').hide();
|
||
$('#addressInfo').show();
|
||
|
||
// 字段映射 - 支持多种格式
|
||
const name = address.receiver_name || address.name || '';
|
||
const phone = address.receiver_phone || address.phone || '';
|
||
const province = address.province_name || address.provinceName || address.province || '';
|
||
const city = address.city_name || address.cityName || address.city || '';
|
||
const district = address.district_name || address.districtName || address.district || '';
|
||
const detail = address.detail_address || address.detailAddress || address.detail || '';
|
||
const isDefault = address.is_default || address.isDefault;
|
||
const tag = address.address_tag || address.addressTag || (isDefault ? '默认' : '');
|
||
|
||
$('#addressName').text(name);
|
||
$('#addressPhone').text(phone);
|
||
$('#addressDetail').text(`${province} ${city} ${district} ${detail}`);
|
||
|
||
if (tag) {
|
||
$('#addressTag').text(tag).show();
|
||
} else {
|
||
$('#addressTag').hide();
|
||
}
|
||
}
|
||
|
||
// 加载优惠券
|
||
function loadCoupons() {
|
||
const startTime = Date.now();
|
||
console.log('\n🎫 加载优惠券(异步,不阻塞页面)...');
|
||
|
||
// 计算总价(支持嵌套结构)
|
||
const totalAmount = checkoutState.cartItems.reduce((sum, item) => {
|
||
const price = parseInt(
|
||
item.price ||
|
||
item.sale_price ||
|
||
item.salePrice ||
|
||
(item.sku && item.sku.price) ||
|
||
(item.product && item.product.price) ||
|
||
0
|
||
);
|
||
const quantity = item.quantity || 1;
|
||
return sum + (price * quantity);
|
||
}, 0);
|
||
|
||
console.log(' 订单总额(分):', totalAmount);
|
||
|
||
// 如果订单金额为0,不加载优惠券
|
||
if (totalAmount <= 0) {
|
||
console.log(' 订单金额为0,跳过加载优惠券\n');
|
||
checkoutState.coupons = [];
|
||
updateCouponDisplay();
|
||
return;
|
||
}
|
||
|
||
// 使用订单可用优惠券 API,添加超时控制
|
||
const timeoutPromise = new Promise((_, reject) => {
|
||
setTimeout(() => reject(new Error('请求超时')), 5000); // 5秒超时
|
||
});
|
||
|
||
const apiPromise = API.get('/coupons/order/available', {
|
||
order_amount: totalAmount
|
||
});
|
||
|
||
Promise.race([apiPromise, timeoutPromise])
|
||
.then(data => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.log(` ✓ 优惠券 API: 完成 (耗时: ${loadTime}ms)\n`);
|
||
|
||
// 检查数据是否为空
|
||
if (!data) {
|
||
console.warn(' ⚠️ 优惠券 API 返回空数据\n');
|
||
checkoutState.coupons = [];
|
||
updateCouponDisplay();
|
||
return;
|
||
}
|
||
|
||
// 支持多种数据格式
|
||
let coupons = [];
|
||
if (Array.isArray(data)) {
|
||
coupons = data;
|
||
} else if (data.data) {
|
||
coupons = Array.isArray(data.data) ? data.data : (data.data.list || data.data.coupons || []);
|
||
} else if (data.list) {
|
||
coupons = data.list;
|
||
} else if (data.coupons) {
|
||
coupons = data.coupons;
|
||
}
|
||
|
||
console.log(' 优惠券数量:', coupons.length, '\n');
|
||
checkoutState.coupons = coupons;
|
||
|
||
// 更新优惠券显示
|
||
updateCouponDisplay();
|
||
})
|
||
.catch(error => {
|
||
const loadTime = Date.now() - startTime;
|
||
console.error(` ✗ 优惠券 API: 失败 (耗时: ${loadTime}ms)`, error.message);
|
||
console.log(' 优惠券加载失败不影响结算,继续使用\n');
|
||
// 优惠券加载失败不影响结算流程,只是没有优惠券可用
|
||
checkoutState.coupons = [];
|
||
updateCouponDisplay();
|
||
});
|
||
}
|
||
|
||
// 更新优惠券显示
|
||
function updateCouponDisplay() {
|
||
const availableCount = checkoutState.coupons.length;
|
||
|
||
if (checkoutState.selectedCoupon) {
|
||
const discount = parseInt(checkoutState.selectedCoupon.discount_amount || 0);
|
||
$('#couponText').text(`-¥${PriceUtils.fenToYuan(discount).toFixed(2)}`);
|
||
} else if (availableCount > 0) {
|
||
$('#couponText').text(`${availableCount}张可用`);
|
||
} else {
|
||
$('#couponText').text('暂无可用优惠券');
|
||
}
|
||
}
|
||
|
||
// 更新价格汇总
|
||
function updatePriceSummary() {
|
||
// 计算商品总额(支持嵌套结构)
|
||
const subtotal = checkoutState.cartItems.reduce((sum, item) => {
|
||
const price = parseInt(
|
||
item.price ||
|
||
item.sale_price ||
|
||
item.salePrice ||
|
||
(item.sku && item.sku.price) ||
|
||
(item.product && item.product.price) ||
|
||
0
|
||
);
|
||
const quantity = item.quantity || 1;
|
||
return sum + (price * quantity);
|
||
}, 0);
|
||
|
||
// 运费(满一定金额免运费)
|
||
const shippingFee = subtotal >= 5900 ? 0 : 0; // 满 59 元免运费
|
||
|
||
// 活动优惠(从商品信息中获取)
|
||
const promotionAmount = 0; // TODO: 从 API 获取
|
||
|
||
// 优惠券优惠
|
||
const couponAmount = checkoutState.selectedCoupon ?
|
||
parseInt(checkoutState.selectedCoupon.discount_amount || 0) : 0;
|
||
|
||
// 应付总额
|
||
const totalAmount = subtotal + shippingFee - promotionAmount - couponAmount;
|
||
|
||
// 更新显示
|
||
$('#subtotalAmount').text(`¥${PriceUtils.fenToYuan(subtotal).toFixed(2)}`);
|
||
|
||
if (shippingFee > 0) {
|
||
$('#shippingAmount').text(`¥${PriceUtils.fenToYuan(shippingFee).toFixed(2)}`);
|
||
} else {
|
||
$('#shippingAmount').text('免运费');
|
||
}
|
||
|
||
if (promotionAmount > 0) {
|
||
$('#promotionRow').show();
|
||
$('#promotionAmount').text(`-¥${PriceUtils.fenToYuan(promotionAmount).toFixed(2)}`);
|
||
} else {
|
||
$('#promotionRow').hide();
|
||
}
|
||
|
||
if (couponAmount > 0) {
|
||
$('#couponRow').show();
|
||
$('#couponAmount').text(`-¥${PriceUtils.fenToYuan(couponAmount).toFixed(2)}`);
|
||
} else {
|
||
$('#couponRow').hide();
|
||
}
|
||
|
||
$('#totalAmount').text(`¥${PriceUtils.fenToYuan(totalAmount).toFixed(2)}`);
|
||
}
|
||
|
||
// 绑定事件
|
||
function bindCheckoutEvents() {
|
||
// 选择地址按钮
|
||
$('#selectAddressBtn, #changeAddressBtn').on('click', function() {
|
||
showAddressModal();
|
||
});
|
||
|
||
// 编辑选中的地址
|
||
$('#editSelectedAddressBtn').on('click', function() {
|
||
if (checkoutState.selectedAddress) {
|
||
editAddress(checkoutState.selectedAddress);
|
||
}
|
||
});
|
||
|
||
// 关闭地址弹窗
|
||
$('#closeAddressModal').on('click', function() {
|
||
hideAddressModal();
|
||
});
|
||
|
||
// 选择优惠券
|
||
$('#couponSelector').on('click', function() {
|
||
if (checkoutState.coupons.length === 0) {
|
||
Toast.info('暂无可用优惠券');
|
||
return;
|
||
}
|
||
showCouponModal();
|
||
});
|
||
|
||
// 关闭优惠券弹窗
|
||
$('#closeCouponModal').on('click', function() {
|
||
hideCouponModal();
|
||
});
|
||
|
||
// 确认优惠券
|
||
$('#confirmCouponBtn').on('click', function() {
|
||
hideCouponModal();
|
||
updatePriceSummary();
|
||
});
|
||
|
||
// 新增地址按钮
|
||
$('#addAddressBtn').on('click', function() {
|
||
hideAddressModal();
|
||
showAddAddressModal();
|
||
});
|
||
|
||
// 关闭新增地址弹窗
|
||
$('#closeAddAddressModal, #cancelAddAddressBtn').on('click', function() {
|
||
hideAddAddressModal();
|
||
});
|
||
|
||
// 保存地址
|
||
$('#saveAddressBtn').on('click', function() {
|
||
saveNewAddress();
|
||
});
|
||
|
||
// 提交订单
|
||
$('#payNowBtn').on('click', function(e) {
|
||
e.preventDefault();
|
||
submitOrder();
|
||
});
|
||
|
||
// 选择支付方式
|
||
$('.payment-method:not(.disabled)').on('click', function() {
|
||
$('.payment-method').removeClass('active');
|
||
$(this).addClass('active');
|
||
checkoutState.paymentMethod = $(this).data('payment');
|
||
});
|
||
|
||
// 关闭支付弹窗
|
||
$('#closePaymentModal').on('click', function() {
|
||
hidePaymentModal();
|
||
});
|
||
|
||
// 点击弹窗背景关闭
|
||
$('.modal').on('click', function(e) {
|
||
if ($(e.target).hasClass('modal')) {
|
||
$(this).removeClass('show');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 显示地址选择弹窗
|
||
function showAddressModal() {
|
||
renderAddressList();
|
||
$('#addressModal').addClass('show');
|
||
}
|
||
|
||
function hideAddressModal() {
|
||
$('#addressModal').removeClass('show');
|
||
}
|
||
|
||
// 渲染地址列表
|
||
function renderAddressList() {
|
||
if (checkoutState.addresses.length === 0) {
|
||
$('#addressList').html(`
|
||
<div style="text-align: center; padding: 40px 20px; color: var(--text-light);">
|
||
<p>暂无收货地址</p>
|
||
</div>
|
||
`);
|
||
return;
|
||
}
|
||
|
||
const addressHtml = checkoutState.addresses.map(address => {
|
||
// 字段映射 - 支持多种格式
|
||
const name = address.receiver_name || address.name || '';
|
||
const phone = address.receiver_phone || address.phone || '';
|
||
const province = address.province_name || address.provinceName || address.province || '';
|
||
const city = address.city_name || address.cityName || address.city || '';
|
||
const district = address.district_name || address.districtName || address.district || '';
|
||
const detail = address.detail_address || address.detailAddress || address.detail || '';
|
||
const isDefault = address.is_default || address.isDefault;
|
||
const addressId = address.id || address.address_id || address.addressId;
|
||
const isSelected = checkoutState.selectedAddress &&
|
||
(checkoutState.selectedAddress.id === addressId ||
|
||
checkoutState.selectedAddress.address_id === addressId ||
|
||
checkoutState.selectedAddress.addressId === addressId);
|
||
|
||
return `
|
||
<div class="address-item ${isSelected ? 'selected' : ''}" data-address-id="${addressId}">
|
||
<div class="address-content">
|
||
<div class="address-header">
|
||
<div class="address-user">
|
||
<span class="user-name">${name}</span>
|
||
<span class="user-phone">${phone}</span>
|
||
</div>
|
||
${isDefault ? '<span class="tag tag-default">默认</span>' : ''}
|
||
</div>
|
||
<div class="address-detail">${province} ${city} ${district} ${detail}</div>
|
||
</div>
|
||
<div class="address-operations">
|
||
<button class="btn-icon-action btn-edit-address" data-address-id="${addressId}" title="编辑" onclick="event.stopPropagation()">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
||
</svg>
|
||
</button>
|
||
<button class="btn-icon-action btn-delete-address" data-address-id="${addressId}" title="删除" onclick="event.stopPropagation()">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<polyline points="3 6 5 6 21 6"></polyline>
|
||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
${isSelected ? '<div class="address-selected-icon">✓</div>' : ''}
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
$('#addressList').html(addressHtml);
|
||
|
||
// 绑定地址选择事件
|
||
$('.address-item').on('click', function() {
|
||
const addressId = $(this).data('address-id');
|
||
const address = checkoutState.addresses.find(addr =>
|
||
(addr.id == addressId || addr.address_id == addressId || addr.addressId == addressId)
|
||
);
|
||
|
||
if (address) {
|
||
checkoutState.selectedAddress = address;
|
||
renderSelectedAddress(address);
|
||
hideAddressModal();
|
||
|
||
// 重新加载优惠券(地址可能影响可用优惠券)
|
||
loadCoupons();
|
||
}
|
||
});
|
||
|
||
// 绑定编辑按钮事件
|
||
$('.btn-edit-address').on('click', function(e) {
|
||
e.stopPropagation();
|
||
const addressId = $(this).data('address-id');
|
||
const address = checkoutState.addresses.find(addr =>
|
||
(addr.id == addressId || addr.address_id == addressId || addr.addressId == addressId)
|
||
);
|
||
if (address) {
|
||
editAddress(address);
|
||
}
|
||
});
|
||
|
||
// 绑定删除按钮事件
|
||
$('.btn-delete-address').on('click', function(e) {
|
||
e.stopPropagation();
|
||
const addressId = $(this).data('address-id');
|
||
deleteAddress(addressId);
|
||
});
|
||
}
|
||
|
||
// 显示优惠券选择弹窗
|
||
function showCouponModal() {
|
||
renderCouponList();
|
||
$('#couponModal').addClass('show');
|
||
}
|
||
|
||
function hideCouponModal() {
|
||
$('#couponModal').removeClass('show');
|
||
}
|
||
|
||
// 渲染优惠券列表
|
||
function renderCouponList() {
|
||
if (checkoutState.coupons.length === 0) {
|
||
$('#couponList').html(`
|
||
<div style="text-align: center; padding: 40px 20px; color: var(--text-light);">
|
||
<p>暂无可用优惠券</p>
|
||
</div>
|
||
`);
|
||
return;
|
||
}
|
||
|
||
const couponHtml = checkoutState.coupons.map(coupon => {
|
||
const name = coupon.coupon_name || coupon.name || '优惠券';
|
||
const discount = parseInt(coupon.discount_amount || 0);
|
||
const minAmount = parseInt(coupon.min_amount || 0);
|
||
const isSelected = checkoutState.selectedCoupon &&
|
||
checkoutState.selectedCoupon.coupon_id === coupon.coupon_id;
|
||
|
||
return `
|
||
<div class="coupon-item ${isSelected ? 'selected' : ''}" data-coupon-id="${coupon.coupon_id}">
|
||
<div class="coupon-left">
|
||
<div class="coupon-discount">¥${PriceUtils.fenToYuan(discount).toFixed(0)}</div>
|
||
<div class="coupon-condition">
|
||
${minAmount > 0 ? `满¥${PriceUtils.fenToYuan(minAmount).toFixed(0)}可用` : '无门槛'}
|
||
</div>
|
||
</div>
|
||
<div class="coupon-right">
|
||
<div class="coupon-name">${name}</div>
|
||
<div class="coupon-validity">
|
||
${formatCouponDate(coupon.start_time, coupon.end_time)}
|
||
</div>
|
||
</div>
|
||
${isSelected ? '<div class="coupon-selected-icon">✓</div>' : ''}
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
|
||
// 添加“不使用优惠券”选项
|
||
const noCouponHtml = `
|
||
<div class="coupon-item ${!checkoutState.selectedCoupon ? 'selected' : ''}" data-coupon-id="">
|
||
<div class="coupon-left">
|
||
<div class="coupon-discount" style="font-size: 16px;">不使用</div>
|
||
</div>
|
||
<div class="coupon-right">
|
||
<div class="coupon-name">不使用优惠券</div>
|
||
</div>
|
||
${!checkoutState.selectedCoupon ? '<div class="coupon-selected-icon">✓</div>' : ''}
|
||
</div>
|
||
`;
|
||
|
||
$('#couponList').html(noCouponHtml + couponHtml);
|
||
|
||
// 绑定优惠券选择事件
|
||
$('.coupon-item').on('click', function() {
|
||
const couponId = $(this).data('coupon-id');
|
||
|
||
if (couponId) {
|
||
const coupon = checkoutState.coupons.find(c => c.coupon_id === couponId);
|
||
checkoutState.selectedCoupon = coupon || null;
|
||
} else {
|
||
checkoutState.selectedCoupon = null;
|
||
}
|
||
|
||
// 重新渲染列表显示选中状态
|
||
renderCouponList();
|
||
|
||
// 更新优惠券显示
|
||
updateCouponDisplay();
|
||
});
|
||
}
|
||
|
||
// 格式化优惠券日期
|
||
function formatCouponDate(startTime, endTime) {
|
||
if (!endTime) return '';
|
||
|
||
const end = new Date(endTime);
|
||
return `有效期至 ${end.getFullYear()}-${String(end.getMonth() + 1).padStart(2, '0')}-${String(end.getDate()).padStart(2, '0')}`;
|
||
}
|
||
|
||
// 显示新增地址弹窗
|
||
function showAddAddressModal() {
|
||
// 清空表单
|
||
$('#addAddressForm')[0].reset();
|
||
|
||
// 初始化省份选择器
|
||
initProvinceSelector();
|
||
|
||
// 禁用城市和区县选择器
|
||
$('#newAddressCity').prop('disabled', true).html('<option value="">请选择城市</option>');
|
||
$('#newAddressDistrict').prop('disabled', true).html('<option value="">请选择区县</option>');
|
||
|
||
// 绑定级联事件
|
||
bindRegionCascade();
|
||
|
||
// 绑定字数统计
|
||
bindCharCount();
|
||
|
||
$('#addAddressModal').addClass('show');
|
||
}
|
||
|
||
function hideAddAddressModal() {
|
||
$('#addAddressModal').removeClass('show');
|
||
}
|
||
|
||
// 初始化省份选择器
|
||
function initProvinceSelector() {
|
||
const provinceSelect = $('#newAddressProvince');
|
||
provinceSelect.html('<option value="">请选择省份</option>');
|
||
|
||
ChinaRegions.provinces.forEach(province => {
|
||
provinceSelect.append(`<option value="${province.code}" data-name="${province.name}">${province.name}</option>`);
|
||
});
|
||
}
|
||
|
||
// 绑定级联事件
|
||
function bindRegionCascade() {
|
||
// 省份变化
|
||
$('#newAddressProvince').off('change').on('change', function() {
|
||
const provinceCode = $(this).val();
|
||
const citySelect = $('#newAddressCity');
|
||
const districtSelect = $('#newAddressDistrict');
|
||
|
||
if (provinceCode) {
|
||
const cities = ChinaRegions.getCities(provinceCode);
|
||
citySelect.html('<option value="">请选择城市</option>');
|
||
|
||
cities.forEach(city => {
|
||
citySelect.append(`<option value="${city.code}" data-name="${city.name}">${city.name}</option>`);
|
||
});
|
||
|
||
citySelect.prop('disabled', false);
|
||
districtSelect.html('<option value="">请选择区县</option>').prop('disabled', true);
|
||
} else {
|
||
citySelect.html('<option value="">请选择城市</option>').prop('disabled', true);
|
||
districtSelect.html('<option value="">请选择区县</option>').prop('disabled', true);
|
||
}
|
||
});
|
||
|
||
// 城市变化
|
||
$('#newAddressCity').off('change').on('change', function() {
|
||
const cityCode = $(this).val();
|
||
const districtSelect = $('#newAddressDistrict');
|
||
|
||
if (cityCode) {
|
||
const districts = ChinaRegions.getDistricts(cityCode);
|
||
districtSelect.html('<option value="">请选择区县</option>');
|
||
|
||
if (districts.length > 0) {
|
||
districts.forEach(district => {
|
||
districtSelect.append(`<option value="${district.code}" data-name="${district.name}">${district.name}</option>`);
|
||
});
|
||
districtSelect.prop('disabled', false);
|
||
} else {
|
||
// 没有区县数据时,也启用选择器,但显示提示
|
||
districtSelect.html('<option value="000000">市辖区</option>');
|
||
districtSelect.prop('disabled', false);
|
||
}
|
||
} else {
|
||
districtSelect.html('<option value="">请选择区县</option>').prop('disabled', true);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 绑定字数统计
|
||
function bindCharCount() {
|
||
$('#newAddressDetail').off('input').on('input', function() {
|
||
const length = $(this).val().length;
|
||
$('#detailCharCount').text(length);
|
||
|
||
if (length > 100) {
|
||
$(this).val($(this).val().substring(0, 100));
|
||
$('#detailCharCount').text(100);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 保存新地址
|
||
function saveNewAddress() {
|
||
const name = $('#newAddressName').val().trim();
|
||
const phone = $('#newAddressPhone').val().trim();
|
||
const provinceSelect = $('#newAddressProvince');
|
||
const citySelect = $('#newAddressCity');
|
||
const districtSelect = $('#newAddressDistrict');
|
||
|
||
const provinceCode = provinceSelect.val();
|
||
const provinceName = provinceSelect.find('option:selected').data('name') || provinceSelect.find('option:selected').text();
|
||
const cityCode = citySelect.val();
|
||
const cityName = citySelect.find('option:selected').data('name') || citySelect.find('option:selected').text();
|
||
const districtCode = districtSelect.val();
|
||
const districtName = districtSelect.find('option:selected').data('name') || districtSelect.find('option:selected').text();
|
||
|
||
const detail = $('#newAddressDetail').val().trim();
|
||
const isDefault = $('#newAddressIsDefault').is(':checked');
|
||
|
||
// 验证
|
||
if (!name) {
|
||
Toast.error('请输入收货人姓名');
|
||
return;
|
||
}
|
||
|
||
if (!phone) {
|
||
Toast.error('请输入联系电话');
|
||
return;
|
||
}
|
||
|
||
// 验证手机号格式
|
||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||
if (!phoneRegex.test(phone)) {
|
||
Toast.error('请输入11位有效的手机号');
|
||
return;
|
||
}
|
||
|
||
if (!provinceCode || !cityCode || !districtCode) {
|
||
Toast.error('请选择完整的所在地区');
|
||
return;
|
||
}
|
||
|
||
if (!detail) {
|
||
Toast.error('请输入详细地址');
|
||
return;
|
||
}
|
||
|
||
if (detail.length < 5) {
|
||
Toast.error('详细地址至少需要5个字符');
|
||
return;
|
||
}
|
||
|
||
// 调用 API 保存地址
|
||
API.post('/users/addresses', {
|
||
name: name,
|
||
phone: phone,
|
||
province: provinceName,
|
||
city: cityName,
|
||
district: districtName,
|
||
detail: detail,
|
||
is_default: isDefault ? 1 : 0
|
||
})
|
||
.then(data => {
|
||
Toast.success('地址保存成功');
|
||
hideAddAddressModal();
|
||
|
||
// 重新加载地址列表
|
||
loadAddresses();
|
||
})
|
||
.catch(error => {
|
||
console.error('保存地址失败:', error);
|
||
Toast.error(error.message || '保存失败');
|
||
});
|
||
}
|
||
|
||
// 提交订单
|
||
function submitOrder() {
|
||
// 验证
|
||
if (!checkoutState.selectedAddress) {
|
||
Toast.error('请选择收货地址');
|
||
return;
|
||
}
|
||
|
||
if (checkoutState.cartItems.length === 0) {
|
||
Toast.error('购物车是空的');
|
||
return;
|
||
}
|
||
|
||
// 获取订单备注
|
||
checkoutState.orderNotes = $('#orderNotes').val().trim();
|
||
|
||
// 显示加载状态
|
||
const payBtn = $('#payNowBtn');
|
||
const originalText = payBtn.html();
|
||
payBtn.prop('disabled', true).html('<span>提交中...</span>');
|
||
|
||
// 获取地址ID - 支持多种字段名,转换为数字类型
|
||
const addressId = parseInt(
|
||
checkoutState.selectedAddress.id ||
|
||
checkoutState.selectedAddress.address_id ||
|
||
checkoutState.selectedAddress.addressId
|
||
);
|
||
|
||
// 构造订单数据
|
||
const orderData = {
|
||
address_id: addressId, // 确保是数字类型
|
||
coupon_id: checkoutState.selectedCoupon ? parseInt(checkoutState.selectedCoupon.coupon_id) : null,
|
||
remark: checkoutState.orderNotes,
|
||
items: checkoutState.cartItems.map(item => ({
|
||
product_id: parseInt(item.product_id),
|
||
sku_id: item.sku_id ? parseInt(item.sku_id) : null,
|
||
quantity: parseInt(item.quantity)
|
||
}))
|
||
};
|
||
|
||
console.log('订单数据:', orderData);
|
||
console.log('结算类型:', checkoutState.checkoutType);
|
||
|
||
// 调用创建订单 API
|
||
API.post('/orders', orderData)
|
||
.then(data => {
|
||
console.log('订单创建成功:', data);
|
||
|
||
const orderId = data.data?.order_id || data.order_id || data.data?.orderId || data.orderId;
|
||
|
||
Toast.success('订单创建成功!');
|
||
|
||
// 恢复按钮状态
|
||
payBtn.prop('disabled', false).html(originalText);
|
||
|
||
// 清空购物车(后端已清空)
|
||
if (window.cart && window.cart.updateCartCount) {
|
||
window.cart.updateCartCount();
|
||
}
|
||
|
||
// 根据支付方式处理
|
||
if (checkoutState.paymentMethod === 'wechat' && orderId) {
|
||
// 微信支付,展示二维码
|
||
showWechatPayment(orderId);
|
||
} else {
|
||
// 其他支付方式或无订单ID,跳转到订单详情
|
||
setTimeout(() => {
|
||
if (orderId) {
|
||
window.location.href = `order-detail.html?id=${orderId}`;
|
||
} else {
|
||
window.location.href = 'user-center.html#orders';
|
||
}
|
||
}, 1000);
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('订单创建失败:', error);
|
||
Toast.error(error.message || '订单创建失败');
|
||
|
||
// 恢复按钮状态
|
||
payBtn.prop('disabled', false).html(originalText);
|
||
});
|
||
}
|
||
|
||
// 编辑地址
|
||
function editAddress(address) {
|
||
// 隐藏地址选择弹窗
|
||
hideAddressModal();
|
||
|
||
// 填充表单数据
|
||
const name = address.receiver_name || address.name || '';
|
||
const phone = address.receiver_phone || address.phone || '';
|
||
const province = address.province_name || address.provinceName || address.province || '';
|
||
const city = address.city_name || address.cityName || address.city || '';
|
||
const district = address.district_name || address.districtName || address.district || '';
|
||
const detail = address.detail_address || address.detailAddress || address.detail || '';
|
||
const isDefault = address.is_default || address.isDefault;
|
||
const addressId = address.id || address.address_id || address.addressId;
|
||
|
||
$('#newAddressName').val(name);
|
||
$('#newAddressPhone').val(phone);
|
||
$('#newAddressDetail').val(detail);
|
||
$('#newAddressIsDefault').prop('checked', isDefault);
|
||
|
||
// 初始化省份选择器
|
||
initProvinceSelector();
|
||
|
||
// 绑定级联事件
|
||
bindRegionCascade();
|
||
|
||
// 绑定字数统计
|
||
bindCharCount();
|
||
|
||
// 更新字数显示
|
||
$('#detailCharCount').text(detail.length);
|
||
|
||
// TODO: 需要根据省市区名称反查 code,然后设置选中
|
||
// 这里简化处理,直接根据名称匹配
|
||
setTimeout(() => {
|
||
// 设置省份
|
||
const provinceOption = $('#newAddressProvince option').filter(function() {
|
||
return $(this).data('name') === province || $(this).text() === province;
|
||
});
|
||
if (provinceOption.length > 0) {
|
||
$('#newAddressProvince').val(provinceOption.val()).trigger('change');
|
||
|
||
// 等待城市加载后设置
|
||
setTimeout(() => {
|
||
const cityOption = $('#newAddressCity option').filter(function() {
|
||
return $(this).data('name') === city || $(this).text() === city;
|
||
});
|
||
if (cityOption.length > 0) {
|
||
$('#newAddressCity').val(cityOption.val()).trigger('change');
|
||
|
||
// 等待区县加载后设置
|
||
setTimeout(() => {
|
||
const districtOption = $('#newAddressDistrict option').filter(function() {
|
||
return $(this).data('name') === district || $(this).text() === district;
|
||
});
|
||
if (districtOption.length > 0) {
|
||
$('#newAddressDistrict').val(districtOption.val());
|
||
}
|
||
}, 100);
|
||
}
|
||
}, 100);
|
||
}
|
||
}, 100);
|
||
|
||
// 修改按钮文字和事件
|
||
$('#saveAddressBtn').text('保存修改').off('click').on('click', function() {
|
||
updateAddress(addressId);
|
||
});
|
||
|
||
// 显示弹窗
|
||
$('#addAddressModal').addClass('show');
|
||
}
|
||
|
||
// 更新地址
|
||
function updateAddress(addressId) {
|
||
const name = $('#newAddressName').val().trim();
|
||
const phone = $('#newAddressPhone').val().trim();
|
||
const provinceSelect = $('#newAddressProvince');
|
||
const citySelect = $('#newAddressCity');
|
||
const districtSelect = $('#newAddressDistrict');
|
||
|
||
const provinceCode = provinceSelect.val();
|
||
const provinceName = provinceSelect.find('option:selected').data('name') || provinceSelect.find('option:selected').text();
|
||
const cityCode = citySelect.val();
|
||
const cityName = citySelect.find('option:selected').data('name') || citySelect.find('option:selected').text();
|
||
const districtCode = districtSelect.val();
|
||
const districtName = districtSelect.find('option:selected').data('name') || districtSelect.find('option:selected').text();
|
||
|
||
const detail = $('#newAddressDetail').val().trim();
|
||
const isDefault = $('#newAddressIsDefault').is(':checked');
|
||
|
||
// 验证
|
||
if (!name) {
|
||
Toast.error('请输入收货人姓名');
|
||
return;
|
||
}
|
||
|
||
if (!phone) {
|
||
Toast.error('请输入联系电话');
|
||
return;
|
||
}
|
||
|
||
// 验证手机号格式
|
||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||
if (!phoneRegex.test(phone)) {
|
||
Toast.error('请输入11位有效的手机号');
|
||
return;
|
||
}
|
||
|
||
if (!provinceCode || !cityCode || !districtCode) {
|
||
Toast.error('请选择完整的所在地区');
|
||
return;
|
||
}
|
||
|
||
if (!detail) {
|
||
Toast.error('请输入详细地址');
|
||
return;
|
||
}
|
||
|
||
if (detail.length < 5) {
|
||
Toast.error('详细地址至少需要5个字符');
|
||
return;
|
||
}
|
||
|
||
// 调用 API 更新地址
|
||
API.put(`/users/addresses/${addressId}`, {
|
||
name: name,
|
||
phone: phone,
|
||
province: provinceName,
|
||
city: cityName,
|
||
district: districtName,
|
||
detail: detail,
|
||
is_default: isDefault ? 1 : 0
|
||
})
|
||
.then(data => {
|
||
Toast.success('地址更新成功');
|
||
hideAddAddressModal();
|
||
|
||
// 恢复按钮为新增模式
|
||
$('#saveAddressBtn').text('保存').off('click').on('click', function() {
|
||
saveNewAddress();
|
||
});
|
||
|
||
// 重新加载地址列表
|
||
loadAddresses();
|
||
})
|
||
.catch(error => {
|
||
console.error('更新地址失败:', error);
|
||
Toast.error(error.message || '更新失败');
|
||
});
|
||
}
|
||
|
||
// 删除地址
|
||
function deleteAddress(addressId) {
|
||
Toast.confirm({
|
||
title: '确认删除',
|
||
message: '确定要删除这个地址吗?',
|
||
confirmText: '删除',
|
||
cancelText: '取消',
|
||
confirmColor: '#ff6b6b'
|
||
}).then(confirmed => {
|
||
if (confirmed) {
|
||
API.delete(`/users/addresses/${addressId}`)
|
||
.then(() => {
|
||
Toast.success('地址删除成功');
|
||
|
||
// 如果删除的是当前选中的地址,清空选中状态
|
||
if (checkoutState.selectedAddress &&
|
||
(checkoutState.selectedAddress.id == addressId ||
|
||
checkoutState.selectedAddress.address_id == addressId ||
|
||
checkoutState.selectedAddress.addressId == addressId)) {
|
||
checkoutState.selectedAddress = null;
|
||
$('#noAddress').show();
|
||
$('#addressInfo').hide();
|
||
}
|
||
|
||
// 重新加载地址列表
|
||
loadAddresses();
|
||
})
|
||
.catch(error => {
|
||
console.error('删除地址失败:', error);
|
||
Toast.error(error.message || '删除失败');
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// ===================== 支付相关 =====================
|
||
|
||
// 显示微信支付弹窗
|
||
function showWechatPayment(orderId) {
|
||
console.log('开始微信支付流程,订单ID:', orderId);
|
||
|
||
// 计算总金额
|
||
const totalAmount = calculateTotalAmount();
|
||
|
||
// 显示支付金额
|
||
$('#paymentAmount').text(`¥${PriceUtils.fenToYuan(totalAmount).toFixed(2)}`);
|
||
|
||
// 显示弹窗
|
||
$('#paymentModal').addClass('show');
|
||
|
||
// 调用支付 API 获取二维码
|
||
API.post(`/orders/${orderId}/pay`, {
|
||
payment_method: 'wechat'
|
||
})
|
||
.then(data => {
|
||
console.log('支付响应:', data);
|
||
|
||
// 支持多种数据格式
|
||
const paymentData = data.data || data;
|
||
const qrcodeUrl = paymentData.qrcode_url || paymentData.qrcodeUrl || paymentData.code_url || paymentData.codeUrl;
|
||
|
||
if (qrcodeUrl) {
|
||
// 显示二维码
|
||
displayQRCode(qrcodeUrl);
|
||
|
||
// 开始轮询支付状态
|
||
startPaymentPolling(orderId);
|
||
|
||
// 开始倒计时(5分钟)
|
||
startCountdown(300);
|
||
} else {
|
||
Toast.error('获取支付二维码失败');
|
||
hidePaymentModal();
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('创建支付失败:', error);
|
||
Toast.error(error.message || '创建支付失败');
|
||
hidePaymentModal();
|
||
});
|
||
}
|
||
|
||
// 显示二维码
|
||
function displayQRCode(url) {
|
||
// 替换 loading 为二维码图片
|
||
$('#qrcodeContainer').html(`
|
||
<img src="${url}" alt="微信支付二维码" />
|
||
`);
|
||
}
|
||
|
||
// 开始轮询支付状态
|
||
function startPaymentPolling(orderId) {
|
||
// 清除之前的定时器
|
||
if (checkoutState.paymentTimer) {
|
||
clearInterval(checkoutState.paymentTimer);
|
||
}
|
||
|
||
// 每 2 秒查询一次
|
||
checkoutState.paymentTimer = setInterval(() => {
|
||
checkPaymentStatus(orderId);
|
||
}, 2000);
|
||
}
|
||
|
||
// 检查支付状态
|
||
function checkPaymentStatus(orderId) {
|
||
API.get(`/orders/${orderId}/payment/status`)
|
||
.then(data => {
|
||
const status = data.data?.status || data.status;
|
||
|
||
console.log('支付状态:', status);
|
||
|
||
if (status === 'paid' || status === 'success') {
|
||
// 支付成功
|
||
handlePaymentSuccess(orderId);
|
||
} else if (status === 'failed' || status === 'cancelled') {
|
||
// 支付失败
|
||
handlePaymentFailure();
|
||
}
|
||
// 其他状态继续轮询
|
||
})
|
||
.catch(error => {
|
||
console.error('查询支付状态失败:', error);
|
||
// 继续轮询,不中断
|
||
});
|
||
}
|
||
|
||
// 支付成功处理
|
||
function handlePaymentSuccess(orderId) {
|
||
// 停止轮询
|
||
clearInterval(checkoutState.paymentTimer);
|
||
clearInterval(checkoutState.countdownTimer);
|
||
|
||
// 显示成功消息
|
||
Toast.success('支付成功!');
|
||
|
||
// 关闭弹窗
|
||
hidePaymentModal();
|
||
|
||
// 跳转到订单详情
|
||
setTimeout(() => {
|
||
window.location.href = `order-detail.html?id=${orderId}`;
|
||
}, 1000);
|
||
}
|
||
|
||
// 支付失败处理
|
||
function handlePaymentFailure() {
|
||
// 停止轮询
|
||
clearInterval(checkoutState.paymentTimer);
|
||
clearInterval(checkoutState.countdownTimer);
|
||
|
||
// 显示失败消息
|
||
Toast.error('支付失败,请重试');
|
||
|
||
// 关闭弹窗
|
||
hidePaymentModal();
|
||
}
|
||
|
||
// 开始倒计时
|
||
function startCountdown(seconds) {
|
||
// 清除之前的定时器
|
||
if (checkoutState.countdownTimer) {
|
||
clearInterval(checkoutState.countdownTimer);
|
||
}
|
||
|
||
let remainingSeconds = seconds;
|
||
|
||
// 更新显示
|
||
updateCountdownDisplay(remainingSeconds);
|
||
|
||
// 每秒更新
|
||
checkoutState.countdownTimer = setInterval(() => {
|
||
remainingSeconds--;
|
||
|
||
if (remainingSeconds <= 0) {
|
||
// 倒计时结束
|
||
clearInterval(checkoutState.countdownTimer);
|
||
clearInterval(checkoutState.paymentTimer);
|
||
|
||
Toast.warning('二维码已过期,请重新生成');
|
||
hidePaymentModal();
|
||
} else {
|
||
updateCountdownDisplay(remainingSeconds);
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
// 更新倒计时显示
|
||
function updateCountdownDisplay(seconds) {
|
||
const minutes = Math.floor(seconds / 60);
|
||
const secs = seconds % 60;
|
||
$('#countdownTime').text(`${minutes}:${String(secs).padStart(2, '0')}`);
|
||
}
|
||
|
||
// 隐藏支付弹窗
|
||
function hidePaymentModal() {
|
||
// 清除定时器
|
||
if (checkoutState.paymentTimer) {
|
||
clearInterval(checkoutState.paymentTimer);
|
||
checkoutState.paymentTimer = null;
|
||
}
|
||
|
||
if (checkoutState.countdownTimer) {
|
||
clearInterval(checkoutState.countdownTimer);
|
||
checkoutState.countdownTimer = null;
|
||
}
|
||
|
||
// 关闭弹窗
|
||
$('#paymentModal').removeClass('show');
|
||
|
||
// 重置二维码区域
|
||
$('#qrcodeContainer').html(`
|
||
<div class="qrcode-loading">
|
||
<div class="spinner"></div>
|
||
<p>正在生成二维码...</p>
|
||
</div>
|
||
`);
|
||
}
|
||
|
||
// 计算总金额
|
||
function calculateTotalAmount() {
|
||
// 计算商品总额
|
||
const subtotal = checkoutState.cartItems.reduce((sum, item) => {
|
||
const price = parseInt(
|
||
item.price ||
|
||
item.sale_price ||
|
||
item.salePrice ||
|
||
(item.sku && item.sku.price) ||
|
||
(item.product && item.product.price) ||
|
||
0
|
||
);
|
||
const quantity = item.quantity || 1;
|
||
return sum + (price * quantity);
|
||
}, 0);
|
||
|
||
// 运费
|
||
const shippingFee = subtotal >= 5900 ? 0 : 0;
|
||
|
||
// 优惠券优惠
|
||
const couponAmount = checkoutState.selectedCoupon ?
|
||
parseInt(checkoutState.selectedCoupon.discount_amount || 0) : 0;
|
||
|
||
// 总金额
|
||
return subtotal + shippingFee - couponAmount;
|
||
}
|