546 lines
20 KiB
Plaintext
546 lines
20 KiB
Plaintext
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
|
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
|
<%@ taglib prefix="fn" uri="http://flashsale.org/functions" %>
|
|
|
|
<c:set var="pageTitle" value="订单管理"/>
|
|
<%@ include file="../common/header.jsp" %>
|
|
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<!-- 侧边栏 -->
|
|
<nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
|
|
<div class="position-sticky pt-3">
|
|
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
|
|
<span>管理功能</span>
|
|
</h6>
|
|
<ul class="nav flex-column">
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="${pageContext.request.contextPath}/admin">
|
|
<i class="fas fa-tachometer-alt"></i> 仪表盘
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="${pageContext.request.contextPath}/admin/products">
|
|
<i class="fas fa-box"></i> 商品管理
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="${pageContext.request.contextPath}/admin/flashsales">
|
|
<i class="fas fa-bolt"></i> 秒杀管理
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link active" href="${pageContext.request.contextPath}/admin/orders">
|
|
<i class="fas fa-shopping-cart"></i> 订单管理
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="${pageContext.request.contextPath}/admin/users">
|
|
<i class="fas fa-users"></i> 用户管理
|
|
</a>
|
|
</li>
|
|
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- 主内容区域 -->
|
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
|
<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">
|
|
<div class="btn-group me-2">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="refreshOrders()">
|
|
<i class="fas fa-sync-alt"></i> 刷新
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-success" onclick="exportOrders()">
|
|
<i class="fas fa-download"></i> 导出
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 筛选和搜索 -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-3">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="searchInput" placeholder="搜索订单号/用户...">
|
|
<button class="btn btn-outline-secondary" type="button" onclick="searchOrders()">
|
|
<i class="fas fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="statusFilter" onchange="filterOrders()">
|
|
<option value="">全部状态</option>
|
|
<option value="pending">待支付</option>
|
|
<option value="paid">已支付</option>
|
|
<option value="shipped">已发货</option>
|
|
<option value="completed">已完成</option>
|
|
<option value="cancelled">已取消</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<input type="date" class="form-control" id="dateFilter" onchange="filterOrders()">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select class="form-select" id="sortBy" onchange="sortOrders()">
|
|
<option value="created_at">按创建时间</option>
|
|
<option value="total_amount">按订单金额</option>
|
|
<option value="status">按订单状态</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 订单统计 -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title text-primary" id="totalOrders">0</h5>
|
|
<p class="card-text">总订单数</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title text-success" id="paidOrders">0</h5>
|
|
<p class="card-text">已支付订单</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title text-warning" id="pendingOrders">0</h5>
|
|
<p class="card-text">待处理订单</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body">
|
|
<h5 class="card-title text-info" id="totalAmount">¥0</h5>
|
|
<p class="card-text">总交易额</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 订单列表 -->
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>订单号</th>
|
|
<th>用户</th>
|
|
<th>商品信息</th>
|
|
<th>数量</th>
|
|
<th>总金额</th>
|
|
<th>状态</th>
|
|
<th>创建时间</th>
|
|
<th>操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="ordersTableBody">
|
|
<tr>
|
|
<td colspan="8" class="text-center">
|
|
<i class="fas fa-spinner fa-spin"></i> 加载中...
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
<nav aria-label="订单分页">
|
|
<ul class="pagination justify-content-center" id="pagination">
|
|
<!-- 分页按钮将通过JavaScript生成 -->
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 订单详情模态框 -->
|
|
<div class="modal fade" id="orderDetailModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">订单详情</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body" id="orderDetailContent">
|
|
<!-- 订单详情内容 -->
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.sidebar {
|
|
position: fixed;
|
|
top: 56px;
|
|
bottom: 0;
|
|
left: 0;
|
|
z-index: 100;
|
|
padding: 48px 0 0;
|
|
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
|
|
}
|
|
|
|
.sidebar .nav-link {
|
|
font-weight: 500;
|
|
color: #333;
|
|
}
|
|
|
|
.sidebar .nav-link.active {
|
|
color: #007bff;
|
|
}
|
|
|
|
main {
|
|
margin-left: 240px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
main {
|
|
margin-left: 0;
|
|
}
|
|
|
|
.sidebar {
|
|
position: relative;
|
|
top: 0;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
let currentPage = 1;
|
|
let pageSize = 10;
|
|
let totalPages = 1;
|
|
|
|
$(document).ready(function () {
|
|
loadOrders();
|
|
loadOrderStats();
|
|
});
|
|
|
|
function loadOrders(page = 1) {
|
|
currentPage = page;
|
|
|
|
// 显示加载状态
|
|
$('#orderTableBody').html('<tr><td colspan="8" class="text-center"><i class="fas fa-spinner fa-spin"></i> 加载中...</td></tr>');
|
|
|
|
// 构建请求参数
|
|
let params = {
|
|
page: page,
|
|
size: 10
|
|
};
|
|
|
|
const keyword = $('#searchKeyword').val();
|
|
const status = $('#statusFilter').val();
|
|
|
|
if (keyword && keyword.trim()) {
|
|
params.keyword = keyword.trim();
|
|
}
|
|
|
|
if (status && status !== '') {
|
|
params.status = status;
|
|
}
|
|
|
|
// 调用真实API
|
|
$.get('${pageContext.request.contextPath}/api/admin/orders', params)
|
|
.done(function (response) {
|
|
if (response.success) {
|
|
renderOrdersTable(response.data.orders);
|
|
renderPagination(response.data.currentPage, response.data.totalPages);
|
|
} else {
|
|
$('#orderTableBody').html('<tr><td colspan="8" class="text-center text-danger">获取订单数据失败: ' + response.message + '</td></tr>');
|
|
}
|
|
})
|
|
.fail(function () {
|
|
$('#orderTableBody').html('<tr><td colspan="8" class="text-center text-danger">网络请求失败,请稍后重试</td></tr>');
|
|
});
|
|
}
|
|
|
|
function loadOrderStats() {
|
|
// 调用真实API获取订单统计数据
|
|
$.get('${pageContext.request.contextPath}/api/admin/orders/stats')
|
|
.done(function (response) {
|
|
if (response.success) {
|
|
updateOrderStats(response.data);
|
|
} else {
|
|
console.error('获取订单统计数据失败:', response.message);
|
|
// 显示默认值
|
|
updateOrderStats({});
|
|
}
|
|
})
|
|
.fail(function () {
|
|
console.error('获取订单统计数据请求失败');
|
|
// 显示默认值
|
|
updateOrderStats({});
|
|
});
|
|
}
|
|
|
|
// 更新订单统计数据
|
|
function updateOrderStats(stats) {
|
|
$('#totalOrders').text(formatNumber(stats.totalOrders || 0));
|
|
$('#paidOrders').text(formatNumber(stats.paidOrders || 0));
|
|
$('#pendingOrders').text(formatNumber(stats.pendingOrders || 0));
|
|
$('#totalAmount').text('¥' + formatNumber(stats.totalAmount || 0));
|
|
}
|
|
|
|
function renderOrdersTable(orders) {
|
|
let html = '';
|
|
|
|
if (orders.length === 0) {
|
|
html = '<tr><td colspan="8" class="text-center">暂无订单数据</td></tr>';
|
|
} else {
|
|
orders.forEach(order => {
|
|
const statusText = getOrderStatusText(order.status);
|
|
const statusClass = getOrderStatusClass(order.status);
|
|
|
|
html += `
|
|
<tr>
|
|
<td>
|
|
<div class="fw-bold">` + order.id + `</div>
|
|
` + (order.isFlashSale ? '<small class="text-danger"><i class="fas fa-bolt"></i> 秒杀订单</small>' : '') + `
|
|
</td>
|
|
<td>` + order.username + `</td>
|
|
<td>` + order.productName + `</td>
|
|
<td>` + order.quantity + `</td>
|
|
<td class="fw-bold">¥` + formatNumber(order.totalAmount || 0) + `</td>
|
|
<td>
|
|
<span class="badge ` + statusClass + `">
|
|
` + statusText + `
|
|
</span>
|
|
</td>
|
|
<td>` + formatDateTime(order.createdAt) + `</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-primary" onclick="viewOrderDetail('` + order.id + `')" title="查看详情">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
` + (order.status === 2 ?
|
|
'<button class="btn btn-outline-success" onclick="shipOrder(\'' + order.id + '\')" title="发货"><i class="fas fa-shipping-fast"></i></button>' : '') + `
|
|
` + (order.status === 1 ?
|
|
'<button class="btn btn-outline-danger" onclick="cancelOrder(\'' + order.id + '\')" title="取消"><i class="fas fa-times"></i></button>' : '') + `
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
}
|
|
|
|
$('#ordersTableBody').html(html);
|
|
}
|
|
|
|
function getOrderStatusText(status) {
|
|
switch (status) {
|
|
case 'pending':
|
|
return '待支付';
|
|
case 'paid':
|
|
return '已支付';
|
|
case 'shipped':
|
|
return '已发货';
|
|
case 'completed':
|
|
return '已完成';
|
|
case 'cancelled':
|
|
return '已取消';
|
|
default:
|
|
return '未知';
|
|
}
|
|
}
|
|
|
|
function getOrderStatusClass(status) {
|
|
switch (status) {
|
|
case 'pending':
|
|
return 'bg-warning';
|
|
case 'paid':
|
|
return 'bg-success';
|
|
case 'shipped':
|
|
return 'bg-info';
|
|
case 'completed':
|
|
return 'bg-primary';
|
|
case 'cancelled':
|
|
return 'bg-secondary';
|
|
default:
|
|
return 'bg-light';
|
|
}
|
|
}
|
|
|
|
function renderPagination(total, pageSize) {
|
|
totalPages = Math.ceil(total / pageSize);
|
|
let html = '';
|
|
|
|
// 上一页
|
|
html += `
|
|
<li class="page-item ` + (currentPage === 1 ? 'disabled' : '') + `">
|
|
<a class="page-link" href="#" onclick="loadOrders(` + (currentPage - 1) + `)">上一页</a>
|
|
</li>
|
|
`;
|
|
|
|
// 页码
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
html += `
|
|
<li class="page-item ` + (i === currentPage ? 'active' : '') + `">
|
|
<a class="page-link" href="#" onclick="loadOrders(` + i + `)">` + i + `</a>
|
|
</li>
|
|
`;
|
|
}
|
|
|
|
// 下一页
|
|
html += `
|
|
<li class="page-item ` + (currentPage === totalPages ? 'disabled' : '') + `">
|
|
<a class="page-link" href="#" onclick="loadOrders(` + (currentPage + 1) + `)">下一页</a>
|
|
</li>
|
|
`;
|
|
|
|
$('#pagination').html(html);
|
|
}
|
|
|
|
function refreshOrders() {
|
|
$('#ordersTableBody').html('<tr><td colspan="8" class="text-center"><i class="fas fa-spinner fa-spin"></i> 加载中...</td></tr>');
|
|
loadOrders(currentPage);
|
|
loadOrderStats();
|
|
}
|
|
|
|
function searchOrders() {
|
|
const keyword = $('#searchInput').val();
|
|
console.log('搜索订单:', keyword);
|
|
loadOrders(1);
|
|
}
|
|
|
|
function filterOrders() {
|
|
const status = $('#statusFilter').val();
|
|
const date = $('#dateFilter').val();
|
|
console.log('筛选订单:', {status, date});
|
|
loadOrders(1);
|
|
}
|
|
|
|
function sortOrders() {
|
|
const sortBy = $('#sortBy').val();
|
|
console.log('排序方式:', sortBy);
|
|
loadOrders(1);
|
|
}
|
|
|
|
function viewOrderDetail(orderId) {
|
|
console.log('查看订单详情:', orderId);
|
|
|
|
// 模拟获取订单详情
|
|
const orderDetail = `
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>订单信息</h6>
|
|
<table class="table table-sm">
|
|
<tr><td>订单号:</td><td>` + orderId + `</td></tr>
|
|
<tr><td>用户:</td><td>demo1</td></tr>
|
|
<tr><td>状态:</td><td><span class="badge bg-success">已支付</span></td></tr>
|
|
<tr><td>创建时间:</td><td>2025-06-29 10:30:15</td></tr>
|
|
<tr><td>支付时间:</td><td>2025-06-29 10:31:20</td></tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>商品信息</h6>
|
|
<table class="table table-sm">
|
|
<tr><td>商品名称:</td><td>iPhone 15 Pro Max</td></tr>
|
|
<tr><td>单价:</td><td>¥8,888.00</td></tr>
|
|
<tr><td>数量:</td><td>1</td></tr>
|
|
<tr><td>总金额:</td><td class="fw-bold">¥8,888.00</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<h6>收货地址</h6>
|
|
<p>北京市朝阳区xxx街道xxx号xxx小区xxx楼xxx室<br>
|
|
收货人: 张三 13800138001</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
$('#orderDetailContent').html(orderDetail);
|
|
$('#orderDetailModal').modal('show');
|
|
}
|
|
|
|
function shipOrder(orderId) {
|
|
if (confirm('确定要将此订单标记为已发货吗?')) {
|
|
console.log('发货订单:', orderId);
|
|
|
|
setTimeout(function () {
|
|
alert('订单已标记为已发货!');
|
|
refreshOrders();
|
|
}, 1000);
|
|
}
|
|
}
|
|
|
|
function cancelOrder(orderId) {
|
|
if (confirm('确定要取消此订单吗?此操作不可恢复。')) {
|
|
console.log('取消订单:', orderId);
|
|
|
|
setTimeout(function () {
|
|
alert('订单已取消!');
|
|
refreshOrders();
|
|
}, 1000);
|
|
}
|
|
}
|
|
|
|
function exportOrders() {
|
|
console.log('导出订单数据');
|
|
alert('订单数据导出功能开发中...');
|
|
}
|
|
|
|
// 工具函数
|
|
function formatNumber(num) {
|
|
if (num === null || num === undefined) return '0';
|
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
|
|
function formatDateTime(dateTime) {
|
|
if (!dateTime) return '';
|
|
return new Date(dateTime).toLocaleString('zh-CN');
|
|
}
|
|
|
|
function getOrderStatusClass(status) {
|
|
switch (status) {
|
|
case 1:
|
|
return 'bg-warning'; // 待支付
|
|
case 2:
|
|
return 'bg-success'; // 已支付
|
|
case 3:
|
|
return 'bg-info'; // 已发货
|
|
case 4:
|
|
return 'bg-primary'; // 已完成
|
|
case 5:
|
|
return 'bg-danger'; // 已取消
|
|
default:
|
|
return 'bg-secondary';
|
|
}
|
|
}
|
|
|
|
function getOrderStatusText(status) {
|
|
switch (status) {
|
|
case 1:
|
|
return '待支付';
|
|
case 2:
|
|
return '已支付';
|
|
case 3:
|
|
return '已发货';
|
|
case 4:
|
|
return '已完成';
|
|
case 5:
|
|
return '已取消';
|
|
default:
|
|
return '未知';
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<%@ include file="../common/footer.jsp" %>
|