387 lines
15 KiB
HTML
387 lines
15 KiB
HTML
|
|
{% extends "base.html" %}
|
|||
|
|
|
|||
|
|
{% block title %}订单详情 - {{ config.SITE_NAME or '软件授权管理系统' }}{% endblock %}
|
|||
|
|
|
|||
|
|
{% block extra_css %}
|
|||
|
|
<style>
|
|||
|
|
.order-status-0 { color: #ffc107; } /* 待支付 */
|
|||
|
|
.order-status-1 { color: #28a745; } /* 已支付 */
|
|||
|
|
.order-status-2 { color: #dc3545; } /* 已取消 */
|
|||
|
|
.order-status-3 { color: #6c757d; } /* 已完成 */
|
|||
|
|
|
|||
|
|
.order-info-card {
|
|||
|
|
border-left: 3px solid #667eea;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-group {
|
|||
|
|
margin-bottom: 1rem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-label {
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #6c757d;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-value {
|
|||
|
|
font-size: 1.1rem;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
{% endblock %}
|
|||
|
|
|
|||
|
|
{% block content %}
|
|||
|
|
<div class="container-fluid">
|
|||
|
|
<!-- 页面标题 -->
|
|||
|
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
|||
|
|
<h1 class="h2">订单详情</h1>
|
|||
|
|
<div class="btn-toolbar mb-2 mb-md-0">
|
|||
|
|
<button type="button" class="btn btn-sm btn-outline-secondary me-2" onclick="window.history.back()">
|
|||
|
|
<i class="fas fa-arrow-left me-1"></i>返回
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 订单信息 -->
|
|||
|
|
<div class="row">
|
|||
|
|
<div class="col-lg-8">
|
|||
|
|
<div class="card shadow-sm order-info-card">
|
|||
|
|
<div class="card-header">
|
|||
|
|
<h5 class="mb-0">订单信息</h5>
|
|||
|
|
</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="row">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">订单号</div>
|
|||
|
|
<div class="info-value" id="orderNumber">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">产品</div>
|
|||
|
|
<div class="info-value" id="productName">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">套餐</div>
|
|||
|
|
<div class="info-value" id="packageName">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">数量</div>
|
|||
|
|
<div class="info-value" id="quantity">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">金额</div>
|
|||
|
|
<div class="info-value" id="amount">-</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">状态</div>
|
|||
|
|
<div class="info-value">
|
|||
|
|
<span class="badge bg-secondary" id="statusBadge">-</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">联系人</div>
|
|||
|
|
<div class="info-value" id="contactPerson">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">手机号</div>
|
|||
|
|
<div class="info-value" id="phone">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">支付方式</div>
|
|||
|
|
<div class="info-value" id="paymentMethod">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">支付时间</div>
|
|||
|
|
<div class="info-value" id="paymentTime">-</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="col-lg-4">
|
|||
|
|
<div class="card shadow-sm">
|
|||
|
|
<div class="card-header">
|
|||
|
|
<h5 class="mb-0">操作记录</h5>
|
|||
|
|
</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">创建时间</div>
|
|||
|
|
<div class="info-value" id="createTime">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="info-group">
|
|||
|
|
<div class="info-label">更新时间</div>
|
|||
|
|
<div class="info-value" id="updateTime">-</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<hr>
|
|||
|
|
|
|||
|
|
<div class="d-grid gap-2">
|
|||
|
|
<button type="button" class="btn btn-success" id="confirmPaymentBtn" style="display: none;" onclick="confirmPayment()">
|
|||
|
|
<i class="fas fa-check-circle me-1"></i>确认支付
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-secondary" id="completeOrderBtn" style="display: none;" onclick="completeOrder()">
|
|||
|
|
<i class="fas fa-flag-checkered me-1"></i>完成订单
|
|||
|
|
</button>
|
|||
|
|
<button type="button" class="btn btn-danger" id="cancelOrderBtn" style="display: none;" onclick="cancelOrder()">
|
|||
|
|
<i class="fas fa-times-circle me-1"></i>取消订单
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
{% endblock %}
|
|||
|
|
|
|||
|
|
{% block extra_js %}
|
|||
|
|
<script>
|
|||
|
|
// 格式化日期
|
|||
|
|
function formatDate(dateString) {
|
|||
|
|
if (!dateString) return '-';
|
|||
|
|
const date = new Date(dateString);
|
|||
|
|
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示加载动画
|
|||
|
|
function showLoading() {
|
|||
|
|
document.getElementById('loading').style.display = 'block';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 隐藏加载动画
|
|||
|
|
function hideLoading() {
|
|||
|
|
document.getElementById('loading').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示通知
|
|||
|
|
function showNotification(message, type = 'info') {
|
|||
|
|
// 创建通知元素
|
|||
|
|
const alertDiv = document.createElement('div');
|
|||
|
|
const alertType = type === 'error' ? 'danger' : type;
|
|||
|
|
alertDiv.className = `alert alert-${alertType} alert-dismissible fade show`;
|
|||
|
|
alertDiv.innerHTML = `
|
|||
|
|
${message}
|
|||
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|||
|
|
`;
|
|||
|
|
alertDiv.style.marginBottom = '10px';
|
|||
|
|
|
|||
|
|
// 插入到通知容器中
|
|||
|
|
const container = document.getElementById('notification-container') || document.querySelector('main');
|
|||
|
|
if (container) {
|
|||
|
|
container.insertBefore(alertDiv, container.firstChild);
|
|||
|
|
|
|||
|
|
// 自动隐藏
|
|||
|
|
setTimeout(() => {
|
|||
|
|
if (alertDiv.parentNode) {
|
|||
|
|
alertDiv.remove();
|
|||
|
|
}
|
|||
|
|
}, 5000);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
<script>
|
|||
|
|
// 获取URL中的订单ID
|
|||
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|||
|
|
const orderIdFromUrl = urlParams.get('order_id');
|
|||
|
|
const orderIdFromPath = "{{ request.view_args.order_id if request.view_args.order_id else 0 }}";
|
|||
|
|
const orderId = orderIdFromUrl ? parseInt(orderIdFromUrl) : (orderIdFromPath ? parseInt(orderIdFromPath) : 0);
|
|||
|
|
|
|||
|
|
// 页面加载完成后初始化
|
|||
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|||
|
|
console.log('订单详情页面加载完成,订单ID:', orderId);
|
|||
|
|
|
|||
|
|
if (orderId && orderId > 0) {
|
|||
|
|
loadOrderDetail(orderId);
|
|||
|
|
} else {
|
|||
|
|
showNotification('订单ID无效', 'error');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 加载订单详情
|
|||
|
|
function loadOrderDetail(orderId) {
|
|||
|
|
showLoading();
|
|||
|
|
|
|||
|
|
fetch(`/api/v1/orders/${orderId}`)
|
|||
|
|
.then(response => response.json())
|
|||
|
|
.then(data => {
|
|||
|
|
hideLoading();
|
|||
|
|
if (data.success) {
|
|||
|
|
renderOrderDetail(data.data);
|
|||
|
|
} else {
|
|||
|
|
showNotification('加载订单详情失败: ' + data.message, 'error');
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
hideLoading();
|
|||
|
|
showNotification('加载订单详情失败,请稍后重试', 'error');
|
|||
|
|
console.error('加载订单详情失败:', error);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 渲染订单详情
|
|||
|
|
function renderOrderDetail(order) {
|
|||
|
|
// 基本信息
|
|||
|
|
document.getElementById('orderNumber').textContent = order.order_number || '-';
|
|||
|
|
document.getElementById('productName').textContent = order.product_name || '-';
|
|||
|
|
document.getElementById('packageName').textContent = order.package_id || '-';
|
|||
|
|
document.getElementById('quantity').textContent = order.quantity || '-';
|
|||
|
|
document.getElementById('amount').textContent = order.amount ? `¥${order.amount.toFixed(2)}` : '-';
|
|||
|
|
document.getElementById('contactPerson').textContent = order.contact_person || '-';
|
|||
|
|
document.getElementById('phone').textContent = order.phone || '-';
|
|||
|
|
document.getElementById('paymentMethod').textContent = order.payment_method || '-';
|
|||
|
|
document.getElementById('paymentTime').textContent = order.payment_time ? formatDate(order.payment_time) : '-';
|
|||
|
|
document.getElementById('createTime').textContent = order.create_time ? formatDate(order.create_time) : '-';
|
|||
|
|
document.getElementById('updateTime').textContent = order.update_time ? formatDate(order.update_time) : '-';
|
|||
|
|
|
|||
|
|
// 状态
|
|||
|
|
const statusBadge = document.getElementById('statusBadge');
|
|||
|
|
statusBadge.textContent = order.status_name || '-';
|
|||
|
|
statusBadge.className = `badge bg-${getStatusClass(order.status)} order-status-${order.status}`;
|
|||
|
|
|
|||
|
|
// 操作按钮
|
|||
|
|
updateActionButtons(order.status);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取状态样式类
|
|||
|
|
function getStatusClass(status) {
|
|||
|
|
const classMap = {
|
|||
|
|
0: 'warning',
|
|||
|
|
1: 'success',
|
|||
|
|
2: 'danger',
|
|||
|
|
3: 'secondary'
|
|||
|
|
};
|
|||
|
|
return classMap[status] || 'secondary';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新操作按钮
|
|||
|
|
function updateActionButtons(status) {
|
|||
|
|
// 隐藏所有按钮
|
|||
|
|
document.getElementById('confirmPaymentBtn').style.display = 'none';
|
|||
|
|
document.getElementById('completeOrderBtn').style.display = 'none';
|
|||
|
|
document.getElementById('cancelOrderBtn').style.display = 'none';
|
|||
|
|
|
|||
|
|
// 根据状态显示相应按钮
|
|||
|
|
switch(status) {
|
|||
|
|
case 0: // 待支付
|
|||
|
|
document.getElementById('confirmPaymentBtn').style.display = 'block';
|
|||
|
|
document.getElementById('cancelOrderBtn').style.display = 'block';
|
|||
|
|
break;
|
|||
|
|
case 1: // 已支付
|
|||
|
|
document.getElementById('completeOrderBtn').style.display = 'block';
|
|||
|
|
document.getElementById('cancelOrderBtn').style.display = 'block';
|
|||
|
|
break;
|
|||
|
|
case 2: // 已取消
|
|||
|
|
case 3: // 已完成
|
|||
|
|
// 不显示任何操作按钮
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确认支付
|
|||
|
|
function confirmPayment() {
|
|||
|
|
if (!confirm('确定要确认该订单已支付吗?')) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
showLoading();
|
|||
|
|
|
|||
|
|
fetch(`/api/v1/orders/${orderId}/confirm-payment`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.then(response => response.json())
|
|||
|
|
.then(data => {
|
|||
|
|
hideLoading();
|
|||
|
|
if (data.success) {
|
|||
|
|
showNotification('订单支付确认成功', 'success');
|
|||
|
|
// 重新加载订单详情
|
|||
|
|
loadOrderDetail(orderId);
|
|||
|
|
} else {
|
|||
|
|
showNotification('订单支付确认失败: ' + data.message, 'error');
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
hideLoading();
|
|||
|
|
showNotification('订单支付确认失败,请稍后重试', 'error');
|
|||
|
|
console.error('订单支付确认失败:', error);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 完成订单
|
|||
|
|
function completeOrder() {
|
|||
|
|
if (!confirm('确定要将该订单标记为已完成吗?')) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
showLoading();
|
|||
|
|
|
|||
|
|
fetch(`/api/v1/orders/${orderId}/complete`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.then(response => response.json())
|
|||
|
|
.then(data => {
|
|||
|
|
hideLoading();
|
|||
|
|
if (data.success) {
|
|||
|
|
showNotification('订单完成操作成功', 'success');
|
|||
|
|
// 重新加载订单详情
|
|||
|
|
loadOrderDetail(orderId);
|
|||
|
|
} else {
|
|||
|
|
showNotification('订单完成操作失败: ' + data.message, 'error');
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
hideLoading();
|
|||
|
|
showNotification('订单完成操作失败,请稍后重试', 'error');
|
|||
|
|
console.error('订单完成操作失败:', error);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 取消订单
|
|||
|
|
function cancelOrder() {
|
|||
|
|
if (!confirm('确定要取消该订单吗?')) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
showLoading();
|
|||
|
|
|
|||
|
|
fetch(`/api/v1/orders/${orderId}/cancel`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.then(response => response.json())
|
|||
|
|
.then(data => {
|
|||
|
|
hideLoading();
|
|||
|
|
if (data.success) {
|
|||
|
|
showNotification('订单取消操作成功', 'success');
|
|||
|
|
// 重新加载订单详情
|
|||
|
|
loadOrderDetail(orderId);
|
|||
|
|
} else {
|
|||
|
|
showNotification('订单取消操作失败: ' + data.message, 'error');
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.catch(error => {
|
|||
|
|
hideLoading();
|
|||
|
|
showNotification('订单取消操作失败,请稍后重试', 'error');
|
|||
|
|
console.error('订单取消操作失败:', error);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
{% endblock %}
|