Files
FlashSaleSystem/src/main/webapp/WEB-INF/views/admin/orders.jsp
2025-07-04 22:45:57 +08:00

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" %>