订单展示支付流程
This commit is contained in:
@@ -181,6 +181,51 @@ public class MessageListenerService {
|
||||
log.debug("推荐替代商品: 用户ID={}, 秒杀ID={}", userId, flashSaleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取Long值
|
||||
*/
|
||||
private Long extractLongValue(Object value) {
|
||||
if (value == null) return null;
|
||||
if (value instanceof Long) return (Long) value;
|
||||
if (value instanceof Integer) return ((Integer) value).longValue();
|
||||
if (value instanceof String) return Long.valueOf((String) value);
|
||||
return Long.valueOf(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取Integer值
|
||||
*/
|
||||
private Integer extractIntegerValue(Object value) {
|
||||
if (value == null) return null;
|
||||
if (value instanceof Integer) return (Integer) value;
|
||||
if (value instanceof Long) return ((Long) value).intValue();
|
||||
if (value instanceof String) return Integer.valueOf((String) value);
|
||||
return Integer.valueOf(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析Redisson消息格式
|
||||
*/
|
||||
private Map<String, Object> parseRedissonMessage(String messageBody) throws Exception {
|
||||
// 处理Redisson序列化的数据格式
|
||||
if (messageBody.startsWith("[") && messageBody.contains("java.util.HashMap")) {
|
||||
// 解析Redisson序列化格式: ["java.util.HashMap",{...}]
|
||||
int startIndex = messageBody.indexOf('{');
|
||||
int endIndex = messageBody.lastIndexOf('}') + 1;
|
||||
if (startIndex > 0 && endIndex > startIndex) {
|
||||
String jsonPart = messageBody.substring(startIndex, endIndex);
|
||||
// 处理Redisson的类型信息格式: ["java.lang.Long",11] -> 11
|
||||
jsonPart = jsonPart.replaceAll("\\[\"java\\.lang\\.(Long|Integer|String)\",([^\\]]+)\\]", "$2");
|
||||
return objectMapper.readValue(jsonPart, Map.class);
|
||||
} else {
|
||||
throw new RuntimeException("无法解析Redisson消息格式");
|
||||
}
|
||||
} else {
|
||||
// 标准JSON格式
|
||||
return objectMapper.readValue(messageBody, Map.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单状态变更监听器
|
||||
*/
|
||||
@@ -189,11 +234,13 @@ public class MessageListenerService {
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
try {
|
||||
String messageBody = new String(message.getBody());
|
||||
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class);
|
||||
log.debug("接收到订单状态变更消息: {}", messageBody);
|
||||
|
||||
Long orderId = Long.valueOf(data.get("orderId").toString());
|
||||
Long userId = Long.valueOf(data.get("userId").toString());
|
||||
Integer status = Integer.valueOf(data.get("status").toString());
|
||||
Map<String, Object> data = parseRedissonMessage(messageBody);
|
||||
|
||||
Long orderId = extractLongValue(data.get("orderId"));
|
||||
Long userId = extractLongValue(data.get("userId"));
|
||||
Integer status = extractIntegerValue(data.get("status"));
|
||||
String action = data.get("action").toString();
|
||||
|
||||
log.info("订单状态变更: 订单ID={}, 用户ID={}, 状态={}, 操作={}",
|
||||
@@ -216,10 +263,12 @@ public class MessageListenerService {
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
try {
|
||||
String messageBody = new String(message.getBody());
|
||||
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class);
|
||||
log.debug("接收到库存变更消息: {}", messageBody);
|
||||
|
||||
Long productId = Long.valueOf(data.get("productId").toString());
|
||||
Integer quantity = Integer.valueOf(data.get("quantity").toString());
|
||||
Map<String, Object> data = parseRedissonMessage(messageBody);
|
||||
|
||||
Long productId = extractLongValue(data.get("productId"));
|
||||
Integer quantity = extractIntegerValue(data.get("quantity"));
|
||||
String operation = data.get("operation").toString();
|
||||
|
||||
log.info("库存变更: 商品ID={}, 数量={}, 操作={}", productId, quantity, operation);
|
||||
@@ -241,10 +290,12 @@ public class MessageListenerService {
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
try {
|
||||
String messageBody = new String(message.getBody());
|
||||
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class);
|
||||
log.debug("接收到秒杀结果消息: {}", messageBody);
|
||||
|
||||
Long userId = Long.valueOf(data.get("userId").toString());
|
||||
Long flashSaleId = Long.valueOf(data.get("flashSaleId").toString());
|
||||
Map<String, Object> data = parseRedissonMessage(messageBody);
|
||||
|
||||
Long userId = extractLongValue(data.get("userId"));
|
||||
Long flashSaleId = extractLongValue(data.get("flashSaleId"));
|
||||
Boolean success = Boolean.valueOf(data.get("success").toString());
|
||||
|
||||
log.info("秒杀结果: 用户ID={}, 秒杀ID={}, 成功={}", userId, flashSaleId, success);
|
||||
@@ -266,9 +317,11 @@ public class MessageListenerService {
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
try {
|
||||
String messageBody = new String(message.getBody());
|
||||
Map<String, Object> data = objectMapper.readValue(messageBody, Map.class);
|
||||
log.debug("接收到用户行为消息: {}", messageBody);
|
||||
|
||||
Long userId = Long.valueOf(data.get("userId").toString());
|
||||
Map<String, Object> data = parseRedissonMessage(messageBody);
|
||||
|
||||
Long userId = extractLongValue(data.get("userId"));
|
||||
String action = data.get("action").toString();
|
||||
|
||||
log.info("用户行为: 用户ID={}, 行为={}", userId, action);
|
||||
|
||||
571
src/main/webapp/WEB-INF/views/order-detail.jsp
Normal file
571
src/main/webapp/WEB-INF/views/order-detail.jsp
Normal file
@@ -0,0 +1,571 @@
|
||||
<%@ 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 my-4">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="${pageContext.request.contextPath}/">首页</a></li>
|
||||
<li class="breadcrumb-item"><a href="${pageContext.request.contextPath}/orders">我的订单</a></li>
|
||||
<li class="breadcrumb-item active">订单详情</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载中状态 -->
|
||||
<div id="loadingOrder" class="text-center py-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
<p class="text-muted mt-2">正在加载订单详情...</p>
|
||||
</div>
|
||||
|
||||
<!-- 订单详情内容 -->
|
||||
<div id="orderDetail" style="display: none;">
|
||||
<!-- 订单状态和基本信息 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
<i class="fas fa-receipt text-primary"></i> 订单信息
|
||||
</h5>
|
||||
<div id="orderActions">
|
||||
<!-- 订单操作按钮将动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td class="text-muted" style="width: 120px;">订单号:</td>
|
||||
<td id="orderNo">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">订单状态:</td>
|
||||
<td><span id="orderStatus" class="badge">-</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">订单类型:</td>
|
||||
<td id="orderType">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">下单时间:</td>
|
||||
<td id="createdAt">-</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table table-borderless">
|
||||
<tr>
|
||||
<td class="text-muted" style="width: 120px;">商品数量:</td>
|
||||
<td id="quantity">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">订单金额:</td>
|
||||
<td class="text-danger fw-bold fs-5" id="totalPrice">¥0.00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-muted">更新时间:</td>
|
||||
<td id="updatedAt">-</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
<i class="fas fa-box text-success"></i> 商品信息
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body" id="productInfo">
|
||||
<!-- 商品信息将动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单操作历史 -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
<i class="fas fa-history text-info"></i> 订单状态记录
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="statusHistory">
|
||||
<!-- 状态历史将动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 错误状态 -->
|
||||
<div id="errorState" class="text-center py-5" style="display: none;">
|
||||
<i class="fas fa-exclamation-triangle fa-4x text-warning mb-3"></i>
|
||||
<h4 class="text-muted">订单信息加载失败</h4>
|
||||
<p class="text-muted" id="errorMessage">请稍后重试或联系客服</p>
|
||||
<a href="${pageContext.request.contextPath}/orders" class="btn btn-primary">
|
||||
<i class="fas fa-arrow-left"></i> 返回订单列表
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 支付确认模态框 -->
|
||||
<div class="modal fade" id="paymentModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<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">
|
||||
<div class="text-center mb-3">
|
||||
<i class="fas fa-credit-card fa-3x text-primary"></i>
|
||||
</div>
|
||||
<p class="text-center">确定要支付此订单吗?</p>
|
||||
<div class="alert alert-info">
|
||||
<small>
|
||||
<i class="fas fa-info-circle"></i>
|
||||
这是模拟支付,99%概率成功
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" onclick="processPayment()">
|
||||
<i class="fas fa-credit-card"></i> 确认支付
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const orderId = ${orderId};
|
||||
let currentOrder = null;
|
||||
|
||||
$(document).ready(function () {
|
||||
loadOrderDetail();
|
||||
});
|
||||
|
||||
// 加载订单详情
|
||||
function loadOrderDetail() {
|
||||
$('#loadingOrder').show();
|
||||
$('#orderDetail').hide();
|
||||
$('#errorState').hide();
|
||||
|
||||
console.log('Loading order detail for orderId:', orderId);
|
||||
|
||||
$.ajax({
|
||||
url: '${pageContext.request.contextPath}/api/order/' + orderId,
|
||||
type: 'GET',
|
||||
success: function (response) {
|
||||
console.log('Order detail response:', response);
|
||||
if (response.success) {
|
||||
currentOrder = response.data;
|
||||
console.log('Current order data:', currentOrder);
|
||||
renderOrderDetail(currentOrder);
|
||||
$('#orderDetail').show();
|
||||
} else {
|
||||
console.error('Failed to get order:', response.message);
|
||||
showError(response.message || '订单信息获取失败');
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error('Error loading order:', xhr.status, xhr.responseText);
|
||||
let errorMessage = '网络错误,请稍后重试';
|
||||
if (xhr.status === 404) {
|
||||
errorMessage = '订单不存在或已被删除';
|
||||
} else if (xhr.status === 401) {
|
||||
errorMessage = '请先登录';
|
||||
setTimeout(() => {
|
||||
window.location.href = '${pageContext.request.contextPath}/login?returnUrl=' + encodeURIComponent(window.location.pathname);
|
||||
}, 1500);
|
||||
} else if (xhr.status === 403) {
|
||||
errorMessage = '无权限查看此订单';
|
||||
}
|
||||
showError(errorMessage);
|
||||
},
|
||||
complete: function () {
|
||||
$('#loadingOrder').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 渲染订单详情
|
||||
function renderOrderDetail(order) {
|
||||
if (!order) {
|
||||
console.error('Order data is null or undefined');
|
||||
showError('订单数据为空');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Rendering order detail:', order);
|
||||
|
||||
// 基本信息
|
||||
$('#orderNo').text('#' + (order.id || 'unknown'));
|
||||
$('#quantity').text((order.quantity || 0) + ' 件');
|
||||
$('#totalPrice').text('¥' + (order.totalPrice ? parseFloat(order.totalPrice).toFixed(2) : '0.00'));
|
||||
$('#createdAt').text(formatDateTime(order.createdAt));
|
||||
$('#updatedAt').text(formatDateTime(order.updatedAt));
|
||||
|
||||
// 订单状态
|
||||
const statusInfo = getStatusInfo(order.status || 0);
|
||||
$('#orderStatus').removeClass().addClass('badge ' + statusInfo.class).text(statusInfo.text);
|
||||
|
||||
// 订单类型
|
||||
const typeText = (order.orderType === 2) ? '秒杀订单' : '普通订单';
|
||||
$('#orderType').text(typeText);
|
||||
|
||||
// 商品信息
|
||||
renderProductInfo(order);
|
||||
|
||||
// 订单操作按钮
|
||||
renderOrderActions(order);
|
||||
|
||||
// 状态历史
|
||||
renderStatusHistory(order);
|
||||
}
|
||||
|
||||
// 渲染商品信息
|
||||
function renderProductInfo(order) {
|
||||
// 计算单价
|
||||
const totalPrice = order.totalPrice ? parseFloat(order.totalPrice) : 0;
|
||||
const quantity = order.quantity || 1;
|
||||
const unitPrice = totalPrice / quantity;
|
||||
|
||||
const productName = order.productName || '未知商品';
|
||||
const productId = order.productId || '-';
|
||||
const productImageUrl = order.productImageUrl || '${pageContext.request.contextPath}/images/default-product.svg';
|
||||
const isFlashSale = order.orderType === 2;
|
||||
|
||||
const productHtml = `
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-2">
|
||||
<img src="` + productImageUrl + `"
|
||||
class="img-fluid rounded" alt="` + productName + `"
|
||||
onerror="this.src='${pageContext.request.contextPath}/images/default-product.svg'; this.onerror=null;"
|
||||
style="max-height: 100px; object-fit: cover;">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="mb-1">` + productName + `</h6>
|
||||
<p class="text-muted small mb-0">商品ID: ` + productId + `</p>
|
||||
` + (isFlashSale ? '<span class="badge bg-danger small">秒杀商品</span>' : '<span class="badge bg-primary small">普通商品</span>') + `
|
||||
</div>
|
||||
<div class="col-md-2 text-center">
|
||||
<span class="text-primary fw-bold">¥` + unitPrice.toFixed(2) + `</span>
|
||||
<br><small class="text-muted">单价</small>
|
||||
</div>
|
||||
<div class="col-md-2 text-center">
|
||||
<span class="badge bg-light text-dark fs-6">× ` + quantity + `</span>
|
||||
<br><small class="text-muted">数量</small>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$('#productInfo').html(productHtml);
|
||||
}
|
||||
|
||||
// 渲染订单操作按钮
|
||||
function renderOrderActions(order) {
|
||||
let actionsHtml = '';
|
||||
|
||||
switch (order.status) {
|
||||
case 1: // 待支付
|
||||
actionsHtml = `
|
||||
<button class="btn btn-primary btn-sm me-2" onclick="showPaymentModal()">
|
||||
<i class="fas fa-credit-card"></i> 立即支付
|
||||
</button>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="cancelOrder()">
|
||||
<i class="fas fa-times"></i> 取消订单
|
||||
</button>
|
||||
`;
|
||||
break;
|
||||
case 2: // 已支付
|
||||
actionsHtml = `
|
||||
<button class="btn btn-info btn-sm" onclick="confirmReceipt()" disabled>
|
||||
<i class="fas fa-truck"></i> 等待发货
|
||||
</button>
|
||||
`;
|
||||
break;
|
||||
case 3: // 已发货
|
||||
actionsHtml = `
|
||||
<button class="btn btn-success btn-sm" onclick="confirmReceipt()">
|
||||
<i class="fas fa-check"></i> 确认收货
|
||||
</button>
|
||||
`;
|
||||
break;
|
||||
case 4: // 已完成
|
||||
actionsHtml = `
|
||||
<span class="text-success">
|
||||
<i class="fas fa-check-circle"></i> 订单已完成
|
||||
</span>
|
||||
`;
|
||||
break;
|
||||
case 5: // 已取消
|
||||
actionsHtml = `
|
||||
<span class="text-muted">
|
||||
<i class="fas fa-ban"></i> 订单已取消
|
||||
</span>
|
||||
`;
|
||||
break;
|
||||
}
|
||||
|
||||
$('#orderActions').html(actionsHtml);
|
||||
}
|
||||
|
||||
// 渲染状态历史
|
||||
function renderStatusHistory(order) {
|
||||
const statusSteps = [
|
||||
{status: 1, text: '订单创建', icon: 'fas fa-plus-circle', time: order.createdAt},
|
||||
{status: 2, text: '支付完成', icon: 'fas fa-credit-card'},
|
||||
{status: 3, text: '商品发货', icon: 'fas fa-truck'},
|
||||
{status: 4, text: '订单完成', icon: 'fas fa-check-circle'}
|
||||
];
|
||||
|
||||
let historyHtml = '<div class="timeline">';
|
||||
statusSteps.forEach((step, index) => {
|
||||
const isActive = order.status >= step.status;
|
||||
const isCurrent = order.status === step.status;
|
||||
const statusClass = isActive ? 'text-success' : 'text-muted';
|
||||
|
||||
historyHtml += `
|
||||
<div class="timeline-item ` + (isActive ? 'active' : '') + `">
|
||||
<div class="timeline-marker">
|
||||
<i class="` + step.icon + ` ` + statusClass + `"></i>
|
||||
</div>
|
||||
<div class="timeline-content">
|
||||
<h6 class="` + statusClass + `">` + step.text + `</h6>
|
||||
` + (step.time ? `<small class="text-muted">` + formatDateTime(step.time) + `</small>` : '') + `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
historyHtml += '</div>';
|
||||
|
||||
$('#statusHistory').html(historyHtml);
|
||||
}
|
||||
|
||||
// 显示支付模态框
|
||||
function showPaymentModal() {
|
||||
$('#paymentModal').modal('show');
|
||||
}
|
||||
|
||||
// 处理支付
|
||||
function processPayment() {
|
||||
$('#paymentModal').modal('hide');
|
||||
|
||||
const payBtn = $('button[onclick="processPayment()"]');
|
||||
const originalText = payBtn.html();
|
||||
payBtn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> 支付中...');
|
||||
|
||||
$.ajax({
|
||||
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/pay',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({}),
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
showMessage('💳 支付成功!', 'success');
|
||||
setTimeout(() => {
|
||||
loadOrderDetail(); // 重新加载订单详情
|
||||
}, 1000);
|
||||
} else {
|
||||
showMessage('❌ 支付失败:' + response.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
showMessage('❌ 支付失败,请重试', 'error');
|
||||
},
|
||||
complete: function () {
|
||||
payBtn.prop('disabled', false).html(originalText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
function cancelOrder() {
|
||||
if (!confirm('确定要取消这个订单吗?\n\n取消后无法恢复。')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/cancel',
|
||||
type: 'POST',
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
showMessage('订单已取消', 'success');
|
||||
setTimeout(() => {
|
||||
loadOrderDetail(); // 重新加载订单详情
|
||||
}, 1000);
|
||||
} else {
|
||||
showMessage('取消失败:' + response.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
showMessage('取消失败,请重试', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 确认收货
|
||||
function confirmReceipt() {
|
||||
if (!confirm('确定已收到商品吗?\n\n确认后订单将标记为完成。')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/confirm',
|
||||
type: 'POST',
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
showMessage('✅ 确认收货成功!', 'success');
|
||||
setTimeout(() => {
|
||||
loadOrderDetail(); // 重新加载订单详情
|
||||
}, 1000);
|
||||
} else {
|
||||
showMessage('确认失败:' + response.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
showMessage('确认失败,请重试', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取状态信息
|
||||
function getStatusInfo(status) {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return {text: '待支付', class: 'bg-warning'};
|
||||
case 2:
|
||||
return {text: '已支付', class: 'bg-info'};
|
||||
case 3:
|
||||
return {text: '已发货', class: 'bg-primary'};
|
||||
case 4:
|
||||
return {text: '已完成', class: 'bg-success'};
|
||||
case 5:
|
||||
return {text: '已取消', class: 'bg-secondary'};
|
||||
default:
|
||||
return {text: '未知状态', class: 'bg-secondary'};
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期时间
|
||||
function formatDateTime(dateTimeStr) {
|
||||
if (!dateTimeStr) return '-';
|
||||
const date = new Date(dateTimeStr);
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// 显示错误
|
||||
function showError(message) {
|
||||
$('#errorMessage').text(message);
|
||||
$('#errorState').show();
|
||||
}
|
||||
|
||||
// 显示消息
|
||||
function showMessage(message, type = 'info') {
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = 'alert alert-' + (type == 'error' ? 'danger' : type) + ' alert-dismissible fade show position-fixed';
|
||||
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
||||
|
||||
alertDiv.innerHTML = message + '<button type="button" class="btn-close" data-bs-dismiss="alert"></button>';
|
||||
|
||||
document.body.appendChild(alertDiv);
|
||||
|
||||
// 3秒后自动消失
|
||||
setTimeout(() => {
|
||||
if (alertDiv.parentNode) {
|
||||
alertDiv.remove();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.timeline {
|
||||
position: relative;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.timeline-item:not(:last-child)::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -22px;
|
||||
top: 20px;
|
||||
bottom: -20px;
|
||||
width: 2px;
|
||||
background: #dee2e6;
|
||||
}
|
||||
|
||||
.timeline-item.active:not(:last-child)::before {
|
||||
background: #28a745;
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
position: absolute;
|
||||
left: -30px;
|
||||
top: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: #fff;
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.timeline-item.active .timeline-marker {
|
||||
border-color: #28a745;
|
||||
background: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
</style>
|
||||
|
||||
<%@ include file="common/footer.jsp" %>
|
||||
@@ -494,11 +494,142 @@
|
||||
// 支付订单
|
||||
function payOrder(orderId) {
|
||||
if (confirm('确定要支付这个订单吗?')) {
|
||||
// 这里可以集成真实的支付接口
|
||||
showMessage('支付功能开发中...', 'info');
|
||||
// 显示支付方式选择模态框
|
||||
showPaymentModal(orderId);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示支付方式选择模态框
|
||||
function showPaymentModal(orderId) {
|
||||
const modalHtml = `
|
||||
<div class="modal fade" id="paymentModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<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">
|
||||
<div class="payment-methods">
|
||||
<div class="form-check mb-3" data-payment-method="alipay">
|
||||
<input class="form-check-input" type="radio" name="paymentMethod" id="alipay" value="alipay">
|
||||
<label class="form-check-label" for="alipay">
|
||||
<i class="fab fa-alipay text-primary"></i> 支付宝
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3" data-payment-method="wechat">
|
||||
<input class="form-check-input" type="radio" name="paymentMethod" id="wechat" value="wechat">
|
||||
<label class="form-check-label" for="wechat">
|
||||
<i class="fab fa-weixin text-success"></i> 微信支付
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3" data-payment-method="unionpay">
|
||||
<input class="form-check-input" type="radio" name="paymentMethod" id="unionpay" value="unionpay">
|
||||
<label class="form-check-label" for="unionpay">
|
||||
<i class="fas fa-credit-card text-info"></i> 银联支付
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<div id="paymentStatus" class="text-muted me-auto">
|
||||
<small>选择支付方式后将自动处理支付</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 移除现有的支付模态框
|
||||
$('#paymentModal').remove();
|
||||
|
||||
// 添加新的支付模态框
|
||||
$('body').append(modalHtml);
|
||||
|
||||
// 设置订单ID到模态框数据属性中
|
||||
$('#paymentModal').attr('data-order-id', orderId);
|
||||
|
||||
// 添加支付方式点击事件
|
||||
$('#paymentModal').on('click', '.form-check[data-payment-method]', function () {
|
||||
const paymentMethod = $(this).attr('data-payment-method');
|
||||
const currentOrderId = $('#paymentModal').attr('data-order-id');
|
||||
selectPaymentMethod(paymentMethod, currentOrderId);
|
||||
});
|
||||
|
||||
// 在模态框关闭时移除事件监听器
|
||||
$('#paymentModal').on('hidden.bs.modal', function () {
|
||||
$(this).off('click');
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// 显示模态框
|
||||
$('#paymentModal').modal('show');
|
||||
}
|
||||
|
||||
// 选择支付方式并直接处理支付
|
||||
function selectPaymentMethod(paymentMethod, orderId) {
|
||||
// 如果orderId未定义,从模态框数据属性中获取
|
||||
if (!orderId || orderId === 'undefined') {
|
||||
orderId = $('#paymentModal').attr('data-order-id');
|
||||
}
|
||||
|
||||
// 验证orderId
|
||||
if (!orderId || orderId === 'undefined') {
|
||||
showMessage('订单ID错误,请重新尝试', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Processing payment for order:', orderId, 'with method:', paymentMethod);
|
||||
|
||||
// 选中对应的单选框
|
||||
$('#paymentModal input[value="' + paymentMethod + '"]').prop('checked', true);
|
||||
|
||||
// 显示支付处理中状态
|
||||
$('#paymentStatus').html('<i class="fas fa-spinner fa-spin text-primary"></i> <small class="text-primary">正在处理支付...</small>');
|
||||
|
||||
// 禁用所有支付方式选项
|
||||
$('.payment-methods .form-check').css('pointer-events', 'none').css('opacity', '0.6');
|
||||
|
||||
// 模拟短暂延迟后调用后端支付接口
|
||||
setTimeout(function () {
|
||||
$.ajax({
|
||||
url: '${pageContext.request.contextPath}/api/order/' + orderId + '/pay',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify({
|
||||
paymentMethod: paymentMethod
|
||||
}),
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
$('#paymentStatus').html('<i class="fas fa-check-circle text-success"></i> <small class="text-success">支付成功!</small>');
|
||||
|
||||
// 2秒后关闭模态框并刷新订单列表
|
||||
setTimeout(function () {
|
||||
$('#paymentModal').modal('hide');
|
||||
showMessage('支付成功!订单状态已更新为已支付', 'success');
|
||||
refreshOrders();
|
||||
}, 2000);
|
||||
} else {
|
||||
$('#paymentStatus').html('<i class="fas fa-times-circle text-danger"></i> <small class="text-danger">支付失败: ' + response.message + '</small>');
|
||||
// 恢复支付方式选项
|
||||
$('.payment-methods .form-check').css('pointer-events', 'auto').css('opacity', '1');
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
let errorMessage = '支付失败,请稍后重试';
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
errorMessage = xhr.responseJSON.message;
|
||||
}
|
||||
$('#paymentStatus').html('<i class="fas fa-times-circle text-danger"></i> <small class="text-danger">' + errorMessage + '</small>');
|
||||
// 恢复支付方式选项
|
||||
$('.payment-methods .form-check').css('pointer-events', 'auto').css('opacity', '1');
|
||||
}
|
||||
});
|
||||
}, 1000); // 1秒延迟模拟支付处理
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
function cancelOrder(orderId) {
|
||||
if (confirm('确定要取消这个订单吗?')) {
|
||||
@@ -626,6 +757,65 @@
|
||||
font-weight: 500;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
/* 支付方式样式 */
|
||||
.payment-methods .form-check {
|
||||
border: 2px solid #dee2e6;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.payment-methods .form-check:hover {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.payment-methods .form-check-input {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.payment-methods .form-check-input:checked + .form-check-label {
|
||||
color: #0d6efd;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.payment-methods .form-check-input:checked {
|
||||
background-color: #0d6efd;
|
||||
border-color: #0d6efd;
|
||||
}
|
||||
|
||||
.payment-methods .form-check:has(.form-check-input:checked) {
|
||||
border-color: #0d6efd;
|
||||
background-color: rgba(13, 110, 253, 0.05);
|
||||
}
|
||||
|
||||
.payment-methods .form-check-label {
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
padding-right: 2.5rem;
|
||||
}
|
||||
|
||||
.payment-methods .form-check-label i {
|
||||
font-size: 1.3rem;
|
||||
margin-right: 0.5rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#paymentStatus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<%@ include file="common/footer.jsp" %>
|
||||
Reference in New Issue
Block a user