// 订单管理页面脚本
let currentOrders = [];
let currentCoupons = [];
let currentOrderId = null;
// DOM 元素
const ordersTableBody = document.getElementById('ordersTableBody');
const couponsTableBody = document.getElementById('couponsTableBody');
const searchInput = document.getElementById('searchOrder');
const statusFilter = document.getElementById('statusFilter');
const orderDetailModal = document.getElementById('orderDetailModal');
const shippingModal = document.getElementById('shippingModal');
const createCouponModal = document.getElementById('createCouponModal');
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
loadOrders();
loadCoupons();
});
// 选项卡切换功能
function showTab(tabName) {
// 隐藏所有选项卡内容
const tabs = document.querySelectorAll('.tab-content');
tabs.forEach(tab => tab.classList.remove('active'));
// 移除所有按钮的活动状态
const buttons = document.querySelectorAll('.tab-btn');
buttons.forEach(btn => btn.classList.remove('active'));
// 显示选中的选项卡内容
document.getElementById(tabName + 'Tab').classList.add('active');
// 激活相应按钮
event.target.classList.add('active');
// 根据选项卡加载数据
if (tabName === 'coupons') {
loadCoupons();
} else if (tabName === 'orders') {
loadOrders();
}
}
// 加载订单列表
async function loadOrders(search = '', status = '') {
try {
showLoading();
const params = new URLSearchParams();
if (search) params.append('search', search);
if (status) params.append('status', status);
const response = await fetch(`/api/admin/orders?${params}`);
const data = await response.json();
if (response.ok) {
currentOrders = data.orders;
updateOrdersTable(data.orders);
updateStats(data.stats);
} else {
throw new Error(data.error || '加载订单失败');
}
} catch (error) {
console.error('Load orders error:', error);
showError('加载订单失败: ' + error.message);
}
}
// 更新订单表格
function updateOrdersTable(orders) {
if (!orders || orders.length === 0) {
ordersTableBody.innerHTML = '
| 暂无订单数据 |
';
return;
}
ordersTableBody.innerHTML = orders.map(order => `
|
${order.order_id}
|
${order.customer_name}
${order.customer_phone || '未提供电话'}
${order.shipping_address ? (order.shipping_address.length > 30 ? order.shipping_address.substring(0, 30) + '...' : order.shipping_address) : '未提供地址'}
|
${order.product_name}
数量: ${order.quantity}
|
$${order.total_amount.toFixed(2)}
|
${getStatusText(order.payment_status, 'payment')}
|
${getStatusText(order.shipping_status, 'shipping')}
|
${formatDate(order.created_at)}
|
|
`).join('');
}
// 更新统计数据
function updateStats(stats) {
if (stats) {
const totalElement = document.getElementById('totalOrders');
const pendingElement = document.getElementById('pendingOrders');
const shippedElement = document.getElementById('shippedOrders');
if (totalElement) totalElement.textContent = stats.total || 0;
if (pendingElement) pendingElement.textContent = stats.pending_ship || 0;
if (shippedElement) shippedElement.textContent = stats.shipped || 0;
}
}
// 搜索订单
function searchOrders() {
const search = searchInput.value.trim();
const status = statusFilter.value;
loadOrders(search, status);
}
// 筛选订单
function filterOrders() {
const search = searchInput.value.trim();
const status = statusFilter.value;
loadOrders(search, status);
}
// 显示订单详情
async function showOrderDetail(orderId) {
try {
const response = await fetch(`/api/orders/${orderId}`);
const order = await response.json();
if (response.ok) {
const detailContent = document.getElementById('orderDetailContent');
detailContent.innerHTML = `
基本信息
订单号:
${order.order_id}
支付ID:
${order.payment_id || '未设置'}
创建时间:
${formatDate(order.created_at)}
更新时间:
${formatDate(order.updated_at)}
客户信息
姓名:
${order.customer_name}
邮箱:
${order.customer_email || '未提供'}
电话:
${order.customer_phone || '未提供'}
收货地址:
${order.shipping_address}
商品信息
产品名称:
${order.product_name}
单价:
$${order.unit_price.toFixed(2)}
数量:
${order.quantity}
总金额:
$${order.total_amount.toFixed(2)}
状态信息
支付状态:
${getStatusText(order.payment_status, 'payment')}
发货状态:
${getStatusText(order.shipping_status, 'shipping')}
${order.tracking_number ? `
快递单号:
${order.tracking_number}
` : ''}
${order.shipping_notes ? `
发货备注:
${order.shipping_notes}
` : ''}
`;
orderDetailModal.style.display = 'flex';
} else {
throw new Error(order.error || '获取订单详情失败');
}
} catch (error) {
console.error('Show order detail error:', error);
alert('获取订单详情失败: ' + error.message);
}
}
// 复制订单收货信息
function copyOrderInfo(orderId) {
const order = currentOrders.find(o => o.order_id === orderId);
if (!order) return;
// 组合收货信息:姓名, 电话, 地址
let infoText = order.customer_name;
if (order.customer_phone) {
infoText += ', ' + order.customer_phone;
}
infoText += ', ' + order.shipping_address;
// 复制到剪贴板
copyText(infoText);
}
// 显示发货模态框
function showShippingModal(orderId) {
currentOrderId = orderId;
const order = currentOrders.find(o => o.order_id === orderId);
if (!order) return;
// 清空表单
document.getElementById('trackingNumber').value = '';
document.getElementById('shippingNotes').value = '';
// 显示模态框
shippingModal.style.display = 'flex';
document.getElementById('trackingNumber').focus();
}
// 关闭发货模态框
function closeShippingModal() {
shippingModal.style.display = 'none';
currentOrderId = null;
}
// 确认发货
async function confirmShipping() {
const trackingNumber = document.getElementById('trackingNumber').value.trim();
const shippingNotes = document.getElementById('shippingNotes').value.trim();
if (!trackingNumber) {
alert('请输入运单号');
document.getElementById('trackingNumber').focus();
return;
}
if (!currentOrderId) {
alert('订单ID错误');
return;
}
try {
const response = await fetch(`/api/admin/orders/${currentOrderId}/shipping`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
shipping_status: 'shipped',
tracking_number: trackingNumber,
shipping_notes: shippingNotes
})
});
const result = await response.json();
if (response.ok) {
alert('订单已成功发货!');
closeShippingModal();
loadOrders(); // 重新加载订单列表
} else {
throw new Error(result.error || '发货失败');
}
} catch (error) {
console.error('Shipping error:', error);
alert('发货失败: ' + error.message);
}
}
// 确认并标记为已发货
function confirmMarkAsShipped(orderId) {
if (confirm('确认标记此订单为已发货吗?')) {
markAsShipped(orderId);
}
}
// 一键标记为已发货
async function markAsShipped(orderId) {
try {
const response = await fetch(`/api/admin/orders/${orderId}/shipping`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
shipping_status: 'shipped'
})
});
const result = await response.json();
if (response.ok) {
alert('订单已标记为发货!');
loadOrders(); // 重新加载订单列表
} else {
throw new Error(result.error || '标记发货失败');
}
} catch (error) {
console.error('Mark as shipped error:', error);
alert('标记发货失败: ' + error.message);
}
}
// 复制文本到剪贴板
async function copyText(text) {
try {
await navigator.clipboard.writeText(text);
// 显示复制成功提示
showCopySuccess();
} catch (err) {
// 降级方案:使用传统方法
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
showCopySuccess();
}
}
// 显示复制成功提示
function showCopySuccess() {
// 创建提示元素
const toast = document.createElement('div');
toast.className = 'copy-toast';
toast.textContent = '已复制到剪贴板';
document.body.appendChild(toast);
// 3秒后移除提示
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, 3000);
}
// 关闭订单详情模态框
function closeOrderDetail() {
orderDetailModal.style.display = 'none';
}
// 获取状态文本
function getStatusText(status, type) {
if (type === 'payment') {
switch(status) {
case 'pending': return '未支付';
case 'finished': return '已支付';
case 'failed': return '支付失败';
case 'confirming': return '确认中';
default: return status || '未知';
}
} else if (type === 'shipping') {
switch(status) {
case 'pending': return '未发货';
case 'shipped': return '已发货';
default: return status || '未知';
}
}
return status || '未知';
}
// 显示加载状态
function showLoading() {
ordersTableBody.innerHTML = '| 加载中... |
';
}
// 显示错误信息
function showError(message) {
ordersTableBody.innerHTML = `| 错误: ${message} |
`;
}
// 格式化日期
function formatDate(dateString) {
if (!dateString) return '未设置';
const date = new Date(dateString);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
// 加载优惠码列表
async function loadCoupons() {
try {
showCouponsLoading();
const response = await fetch('/api/admin/coupons');
const data = await response.json();
if (response.ok) {
currentCoupons = data.coupons;
updateCouponsTable(data.coupons);
} else {
throw new Error(data.error || '加载优惠码失败');
}
} catch (error) {
console.error('Load coupons error:', error);
showCouponsError('加载优惠码失败: ' + error.message);
}
}
// 更新优惠码表格
function updateCouponsTable(coupons) {
if (!coupons || coupons.length === 0) {
couponsTableBody.innerHTML = '| 暂无优惠码数据 |
';
return;
}
couponsTableBody.innerHTML = coupons.map(coupon => `
|
${coupon.code}
|
${coupon.name} |
${coupon.discount_type === 'percentage' ? coupon.discount_value + '%' : '$' + coupon.discount_value.toFixed(2)}
|
${coupon.discount_type === 'percentage' ? '百分比' : '固定金额'}
|
已使用: ${coupon.used_count} / ${coupon.max_uses === 999999 ? '无限' : coupon.max_uses}
${coupon.is_reusable ? '可重复使用' : '一次性使用'}
|
${coupon.is_active ? '启用' : '禁用'}
|
${formatDate(coupon.created_at)}
|
|
`).join('');
}
// 显示创建优惠码模态框
function showCreateCouponModal() {
// 清空表单
document.getElementById('couponCode').value = '';
document.getElementById('couponName').value = '';
document.getElementById('discountType').value = 'percentage';
document.getElementById('discountValue').value = '';
document.getElementById('isReusable').checked = false;
document.getElementById('maxUses').value = '1';
document.getElementById('maxUsesGroup').style.display = 'none';
onDiscountTypeChange(); // 更新提示文本
createCouponModal.style.display = 'flex';
document.getElementById('couponCode').focus();
}
// 关闭创建优惠码模态框
function closeCreateCouponModal() {
createCouponModal.style.display = 'none';
}
// 折扣类型变化处理
function onDiscountTypeChange() {
const discountType = document.getElementById('discountType').value;
const hint = document.getElementById('discountHint');
if (discountType === 'percentage') {
hint.textContent = '输入百分比数值,如:10 表示10%折扣';
} else {
hint.textContent = '输入固定金额,如:5 表示减5美元';
}
}
// 可重复使用变化处理
function onReusableChange() {
const isReusable = document.getElementById('isReusable').checked;
const maxUsesGroup = document.getElementById('maxUsesGroup');
if (isReusable) {
maxUsesGroup.style.display = 'block';
document.getElementById('maxUses').value = '10'; // 默认10次
} else {
maxUsesGroup.style.display = 'none';
document.getElementById('maxUses').value = '1';
}
}
// 创建优惠码
async function createCoupon() {
const code = document.getElementById('couponCode').value.trim();
const name = document.getElementById('couponName').value.trim();
const discountType = document.getElementById('discountType').value;
const discountValue = parseFloat(document.getElementById('discountValue').value);
const isReusable = document.getElementById('isReusable').checked;
const maxUses = parseInt(document.getElementById('maxUses').value);
// 验证表单
if (!code || !name || !discountValue) {
alert('请填写所有必填字段');
return;
}
if (discountValue <= 0) {
alert('折扣值必须大于0');
return;
}
if (discountType === 'percentage' && discountValue > 100) {
alert('百分比折扣不能超过100%');
return;
}
if (isReusable && (!maxUses || maxUses < 1)) {
alert('最大使用次数必须大于0');
return;
}
try {
const response = await fetch('/api/admin/coupons', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
code: code,
name: name,
discount_type: discountType,
discount_value: discountValue,
is_reusable: isReusable,
max_uses: maxUses
})
});
const result = await response.json();
if (response.ok) {
alert('优惠码创建成功!');
closeCreateCouponModal();
loadCoupons(); // 重新加载优惠码列表
} else {
throw new Error(result.error || '创建优惠码失败');
}
} catch (error) {
console.error('Create coupon error:', error);
alert('创建优惠码失败: ' + error.message);
}
}
// 切换优惠码状态
async function toggleCouponStatus(couponId, isActive) {
try {
const response = await fetch(`/api/admin/coupons/${couponId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
is_active: isActive
})
});
const result = await response.json();
if (response.ok) {
alert(`优惠码已${isActive ? '启用' : '禁用'}!`);
loadCoupons(); // 重新加载优惠码列表
} else {
throw new Error(result.error || '更新优惠码状态失败');
}
} catch (error) {
console.error('Toggle coupon status error:', error);
alert('更新优惠码状态失败: ' + error.message);
}
}
// 确认删除优惠码
function confirmDeleteCoupon(couponId) {
const coupon = currentCoupons.find(c => c.id === couponId);
if (!coupon) {
alert('优惠码未找到');
return;
}
const confirmMessage = `确认删除以下优惠码吗?\n\n优惠码:${coupon.code}\n名称:${coupon.name}\n已使用次数:${coupon.used_count}\n\n⚠️ 此操作不可撤销!`;
if (confirm(confirmMessage)) {
deleteCoupon(couponId);
}
}
// 删除优惠码
async function deleteCoupon(couponId) {
try {
const response = await fetch(`/api/admin/coupons/${couponId}`, {
method: 'DELETE'
});
const result = await response.json();
if (response.ok) {
alert('优惠码已成功删除!');
loadCoupons(); // 重新加载优惠码列表
} else {
throw new Error(result.error || '删除优惠码失败');
}
} catch (error) {
console.error('Delete coupon error:', error);
alert('删除优惠码失败: ' + error.message);
}
}
// 显示优惠码加载状态
function showCouponsLoading() {
couponsTableBody.innerHTML = '| 加载中... |
';
}
// 显示优惠码错误信息
function showCouponsError(message) {
couponsTableBody.innerHTML = `| 错误: ${message} |
`;
}
// 确认删除订单
function confirmDeleteOrder(orderId) {
const order = currentOrders.find(o => o.order_id === orderId);
if (!order) {
alert('订单未找到');
return;
}
const confirmMessage = `确认删除以下订单吗?\n\n订单号:${orderId}\n客户:${order.customer_name}\n金额:$${order.total_amount.toFixed(2)} USDT\n\n⚠️ 此操作不可撤销!`;
if (confirm(confirmMessage)) {
deleteOrder(orderId);
}
}
// 删除订单
async function deleteOrder(orderId) {
try {
const response = await fetch(`/api/admin/orders/${orderId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
});
const result = await response.json();
if (response.ok) {
alert('订单已成功删除!');
loadOrders(); // 重新加载订单列表
} else {
throw new Error(result.error || '删除订单失败');
}
} catch (error) {
console.error('Delete order error:', error);
alert('删除订单失败: ' + error.message);
}
}
// 键盘事件监听
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeOrderDetail();
closeShippingModal();
}
});
// 搜索框回车事件
searchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
searchOrders();
}
});