485 lines
17 KiB
JavaScript
485 lines
17 KiB
JavaScript
// 订单管理页面脚本
|
|
let currentOrders = [];
|
|
let currentOrderId = null;
|
|
|
|
// DOM 元素
|
|
const ordersTableBody = document.getElementById('ordersTableBody');
|
|
const searchInput = document.getElementById('searchOrder');
|
|
const statusFilter = document.getElementById('statusFilter');
|
|
const orderDetailModal = document.getElementById('orderDetailModal');
|
|
const shippingModal = document.getElementById('shippingModal');
|
|
|
|
// 页面加载时初始化
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
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 = '<tr><td colspan="8" class="loading">暂无订单数据</td></tr>';
|
|
return;
|
|
}
|
|
|
|
ordersTableBody.innerHTML = orders.map(order => `
|
|
<tr>
|
|
<td>
|
|
<strong>${order.order_id}</strong>
|
|
</td>
|
|
<td>
|
|
<div style="line-height: 1.4;">
|
|
<strong>${order.customer_name}</strong>
|
|
<button class="copy-btn" onclick="copyOrderInfo('${order.order_id}')" title="复制收货信息" style="margin-left: 5px; font-size: 12px;">📋</button><br>
|
|
<small style="color: #666;">${order.customer_phone || '未提供电话'}</small><br>
|
|
<small style="color: #666; font-size: 11px; max-width: 200px; display: inline-block; word-wrap: break-word;">${order.shipping_address ? (order.shipping_address.length > 30 ? order.shipping_address.substring(0, 30) + '...' : order.shipping_address) : '未提供地址'}</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
${order.product_name}<br>
|
|
<small style="color: #888;">数量: ${order.quantity}</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<strong style="color: #ffd700;">$${order.total_amount.toFixed(2)}</strong>
|
|
</td>
|
|
<td>
|
|
<span class="status-badge status-${order.payment_status}">
|
|
${getStatusText(order.payment_status, 'payment')}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="status-badge status-${order.shipping_status}">
|
|
${getStatusText(order.shipping_status, 'shipping')}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
${formatDate(order.created_at)}
|
|
</td>
|
|
<td>
|
|
<button class="action-btn btn-ship" onclick="showShippingModal('${order.order_id}')">
|
|
📦 发货
|
|
</button>
|
|
<button class="action-btn btn-delete" onclick="confirmDeleteOrder('${order.order_id}')" title="删除订单">
|
|
🗑️ 删除
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
`).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 = `
|
|
<div class="detail-section">
|
|
<h4>基本信息</h4>
|
|
<div class="detail-row">
|
|
<span class="detail-label">订单号:</span>
|
|
<span class="detail-value">${order.order_id}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">支付ID:</span>
|
|
<span class="detail-value">${order.payment_id || '未设置'}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">创建时间:</span>
|
|
<span class="detail-value">${formatDate(order.created_at)}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">更新时间:</span>
|
|
<span class="detail-value">${formatDate(order.updated_at)}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="detail-section">
|
|
<h4>客户信息</h4>
|
|
<div class="detail-row">
|
|
<span class="detail-label">姓名:</span>
|
|
<span class="detail-value">${order.customer_name}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">邮箱:</span>
|
|
<span class="detail-value">${order.customer_email || '未提供'}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">电话:</span>
|
|
<span class="detail-value">${order.customer_phone || '未提供'}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">收货地址:</span>
|
|
<span class="detail-value">
|
|
${order.shipping_address}
|
|
<button class="copy-btn" onclick="copyText('${order.shipping_address.replace(/'/g, "\\'")}')">📋</button>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="detail-section">
|
|
<h4>商品信息</h4>
|
|
<div class="detail-row">
|
|
<span class="detail-label">产品名称:</span>
|
|
<span class="detail-value">${order.product_name}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">单价:</span>
|
|
<span class="detail-value">$${order.unit_price.toFixed(2)}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">数量:</span>
|
|
<span class="detail-value">${order.quantity}</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">总金额:</span>
|
|
<span class="detail-value" style="color: #ffd700; font-weight: bold;">$${order.total_amount.toFixed(2)}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="detail-section">
|
|
<h4>状态信息</h4>
|
|
<div class="detail-row">
|
|
<span class="detail-label">支付状态:</span>
|
|
<span class="detail-value">
|
|
<span class="status-badge status-${order.payment_status}">
|
|
${getStatusText(order.payment_status, 'payment')}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">发货状态:</span>
|
|
<span class="detail-value">
|
|
<span class="status-badge status-${order.shipping_status}">
|
|
${getStatusText(order.shipping_status, 'shipping')}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
${order.tracking_number ? `
|
|
<div class="detail-row">
|
|
<span class="detail-label">快递单号:</span>
|
|
<span class="detail-value">${order.tracking_number}</span>
|
|
</div>` : ''}
|
|
${order.shipping_notes ? `
|
|
<div class="detail-row">
|
|
<span class="detail-label">发货备注:</span>
|
|
<span class="detail-value">${order.shipping_notes}</span>
|
|
</div>` : ''}
|
|
</div>
|
|
`;
|
|
|
|
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 = '<tr><td colspan="8" class="loading">加载中...</td></tr>';
|
|
}
|
|
|
|
// 显示错误信息
|
|
function showError(message) {
|
|
ordersTableBody.innerHTML = `<tr><td colspan="8" class="loading" style="color: #ff4757;">错误: ${message}</td></tr>`;
|
|
}
|
|
|
|
// 格式化日期
|
|
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'
|
|
});
|
|
}
|
|
|
|
// 确认删除订单
|
|
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();
|
|
}
|
|
}); |