Files
ai_dianshang/web/assets/js/checkout.js

1426 lines
49 KiB
JavaScript
Raw Permalink Normal View History

2025-11-28 15:18:10 +08:00
// 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;
}