修复文件

This commit is contained in:
2025-07-02 22:39:21 +08:00
parent 3b3ec8ea7d
commit b46312c428
21 changed files with 2233 additions and 650 deletions

View File

@@ -81,6 +81,7 @@
<option value="pending">未开始</option>
<option value="active">进行中</option>
<option value="ended">已结束</option>
<option value="paused">已暂停</option>
</select>
</div>
<div class="col-md-2">
@@ -468,6 +469,9 @@
case 'ended':
queryData.status = 3; // 已结束
break;
case 'paused':
queryData.status = 4; // 已暂停
break;
}
}
@@ -528,15 +532,7 @@
</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="editFlashSale(` + flashSale.id + `)" title="编辑">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-outline-danger" onclick="deleteFlashSale(` + flashSale.id + `)" title="删除">
<i class="fas fa-trash"></i>
</button>
<button class="btn btn-outline-info" onclick="viewFlashSale(` + flashSale.id + `)" title="查看">
<i class="fas fa-eye"></i>
</button>
` + getActionButtons(flashSale) + `
</div>
</td>
</tr>
@@ -555,6 +551,8 @@
return '进行中';
case 3:
return '已结束';
case 4:
return '已暂停';
default:
return '未知';
}
@@ -568,6 +566,8 @@
return 'bg-success'; // 进行中
case 3:
return 'bg-secondary'; // 已结束
case 4:
return 'bg-info'; // 已暂停
default:
return 'bg-secondary';
}
@@ -849,6 +849,163 @@
});
}
// 生成操作按钮
function getActionButtons(flashSale) {
let buttons = '';
// 查看按钮 - 始终显示
buttons += `<button class="btn btn-outline-info" onclick="viewFlashSale(` + flashSale.id + `)" title="查看">
<i class="fas fa-eye"></i>
</button>`;
// 根据状态显示不同的操作按钮
switch (flashSale.status) {
case 1: // 未开始
buttons += `<button class="btn btn-outline-success" onclick="publishFlashSale(` + flashSale.id + `)" title="发布">
<i class="fas fa-play"></i>
</button>`;
buttons += `<button class="btn btn-outline-primary" onclick="editFlashSale(` + flashSale.id + `)" title="编辑">
<i class="fas fa-edit"></i>
</button>`;
buttons += `<button class="btn btn-outline-danger" onclick="deleteFlashSale(` + flashSale.id + `)" title="删除">
<i class="fas fa-trash"></i>
</button>`;
break;
case 2: // 进行中
buttons += `<button class="btn btn-outline-warning" onclick="pauseFlashSale(` + flashSale.id + `)" title="暂停">
<i class="fas fa-pause"></i>
</button>`;
buttons += `<button class="btn btn-outline-danger" onclick="endFlashSale(` + flashSale.id + `)" title="结束">
<i class="fas fa-stop"></i>
</button>`;
break;
case 3: // 已结束
// 已结束的活动只能查看
break;
case 4: // 已暂停
buttons += `<button class="btn btn-outline-success" onclick="resumeFlashSale(` + flashSale.id + `)" title="恢复">
<i class="fas fa-play"></i>
</button>`;
buttons += `<button class="btn btn-outline-danger" onclick="endFlashSale(` + flashSale.id + `)" title="结束">
<i class="fas fa-stop"></i>
</button>`;
break;
default:
// 未知状态,只显示查看按钮
break;
}
return buttons;
}
// 发布秒杀活动
function publishFlashSale(id) {
if (confirm('确定要发布这个秒杀活动吗?发布后活动将生效并开始接受用户参与。')) {
console.log('发布秒杀活动:', id);
$.ajax({
url: '${pageContext.request.contextPath}/api/flashsale/' + id + '/publish',
type: 'POST',
success: function (response) {
if (response.success) {
alert('秒杀活动发布成功!');
refreshFlashSales();
} else {
alert('发布失败: ' + response.message);
}
},
error: function (xhr) {
let errorMessage = '发布失败,请稍后重试';
if (xhr.responseJSON && xhr.responseJSON.message) {
errorMessage = xhr.responseJSON.message;
}
alert(errorMessage);
}
});
}
}
// 暂停秒杀活动
function pauseFlashSale(id) {
if (confirm('确定要暂停这个秒杀活动吗?暂停后用户将无法参与秒杀。')) {
console.log('暂停秒杀活动:', id);
$.ajax({
url: '${pageContext.request.contextPath}/api/flashsale/' + id + '/pause',
type: 'POST',
success: function (response) {
if (response.success) {
alert('秒杀活动暂停成功!');
refreshFlashSales();
} else {
alert('暂停失败: ' + response.message);
}
},
error: function (xhr) {
let errorMessage = '暂停失败,请稍后重试';
if (xhr.responseJSON && xhr.responseJSON.message) {
errorMessage = xhr.responseJSON.message;
}
alert(errorMessage);
}
});
}
}
// 恢复秒杀活动
function resumeFlashSale(id) {
if (confirm('确定要恢复这个秒杀活动吗?恢复后用户将可以继续参与秒杀。')) {
console.log('恢复秒杀活动:', id);
$.ajax({
url: '${pageContext.request.contextPath}/api/flashsale/' + id + '/resume',
type: 'POST',
success: function (response) {
if (response.success) {
alert('秒杀活动恢复成功!');
refreshFlashSales();
} else {
alert('恢复失败: ' + response.message);
}
},
error: function (xhr) {
let errorMessage = '恢复失败,请稍后重试';
if (xhr.responseJSON && xhr.responseJSON.message) {
errorMessage = xhr.responseJSON.message;
}
alert(errorMessage);
}
});
}
}
// 结束秒杀活动
function endFlashSale(id) {
if (confirm('确定要结束这个秒杀活动吗?结束后活动将无法恢复。')) {
console.log('结束秒杀活动:', id);
$.ajax({
url: '${pageContext.request.contextPath}/api/flashsale/' + id + '/end',
type: 'POST',
success: function (response) {
if (response.success) {
alert('秒杀活动结束成功!');
refreshFlashSales();
} else {
alert('结束失败: ' + response.message);
}
},
error: function (xhr) {
let errorMessage = '结束失败,请稍后重试';
if (xhr.responseJSON && xhr.responseJSON.message) {
errorMessage = xhr.responseJSON.message;
}
alert(errorMessage);
}
});
}
}
// 工具函数
function formatDateTime(dateTimeStr) {
if (!dateTimeStr) return '-';

View File

@@ -88,14 +88,30 @@
<i class="fas fa-home"></i> 首页
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="${pageContext.request.contextPath}/products">
<i class="fas fa-shopping-bag"></i> 商品列表
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="productsDropdown" role="button"
data-bs-toggle="dropdown">
<i class="fas fa-shopping-bag"></i> 商品
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="${pageContext.request.contextPath}/products">
<i class="fas fa-th-large"></i> 商品列表
</a></li>
<li><a class="dropdown-item" href="${pageContext.request.contextPath}/search">
<i class="fas fa-search"></i> 商品搜索
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" href="${pageContext.request.contextPath}/category/1">
<i class="fas fa-tags"></i> 商品分类
</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="${pageContext.request.contextPath}/flashsales">
<a class="nav-link text-warning fw-bold" href="${pageContext.request.contextPath}/flashsales">
<i class="fas fa-fire"></i> 秒杀活动
<span class="badge bg-danger ms-1">HOT</span>
</a>
</li>
</ul>
@@ -125,6 +141,9 @@
<li><a class="dropdown-item" href="${pageContext.request.contextPath}/orders">
<i class="fas fa-list-alt"></i> 我的订单
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" href="${pageContext.request.contextPath}/profile">
<i class="fas fa-user-cog"></i> 个人设置
</a></li>

View File

@@ -221,12 +221,17 @@
$(document).ready(function() {
// 加载正在进行的秒杀活动
loadActiveFlashSales();
// 加载热门商品
loadHotProducts();
// 启动性能指标动画
animateCounters();
// 更新购物车数量(如果用户已登录)
<c:if test="${not empty sessionScope.user}">
updateCartCount();
</c:if>
});
// 加载正在进行的秒杀活动
@@ -272,7 +277,7 @@ function renderFlashSales(flashSales) {
<small><i class="fas fa-fire"></i> 秒杀中</small>
</div>
<div class="position-absolute top-0 end-0 bg-warning text-dark px-2 py-1 rounded-start">
<small>${discountPercent}% OFF</small>
<small>` + discountPercent + `% OFF</small>
</div>
</div>
<div class="card-body">
@@ -293,7 +298,9 @@ function renderFlashSales(flashSales) {
<div class="text-danger fw-bold mb-2" id="countdown_${flashSale.id}">
计算中...
</div>
<button class="btn btn-danger btn-sm w-100" onclick="participateFlashSale(${flashSale.id})">
<button class="btn btn-danger btn-sm w-100 flash-sale-btn"
onclick="participateFlashSale(` + flashSale.id + `)"
data-flashsale-id="` + flashSale.id + `">
<i class="fas fa-bolt"></i> 立即抢购
</button>
</div>
@@ -346,8 +353,8 @@ function renderHotProducts(products) {
html += `
<div class="col-lg-3 col-md-6 mb-4">
<div class="card h-100">
<img src="${product.imageUrl || '${pageContext.request.contextPath}/images/default-product.svg'}"
class="card-img-top" alt="${product.name}" style="height: 200px; object-fit: cover;"
<img src="` + (product.imageUrl || '${pageContext.request.contextPath}/images/default-product.svg') + `"
class="card-img-top" alt="` + product.name + `" style="height: 200px; object-fit: cover;"
onerror="this.src='${pageContext.request.contextPath}/images/default-product.svg'; this.onerror=null;">
<div class="card-body">
<h6 class="card-title text-truncate">` + product.name + `</h6>
@@ -370,40 +377,118 @@ function renderHotProducts(products) {
$('#hotProducts').html(html);
}
// 参与秒杀
// 参与秒杀(首页版)
function participateFlashSale(flashSaleId) {
<c:choose>
<c:when test="${not empty sessionScope.user}">
if (confirm('确定要参与这个秒杀活动吗?')) {
$.ajax({
url: '${pageContext.request.contextPath}/api/flashsale/participate',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
flashSaleId: flashSaleId,
quantity: 1
}),
success: function(response) {
if (response.success) {
showMessage('秒杀成功!订单已生成', 'success');
setTimeout(() => {
window.location.href = '${pageContext.request.contextPath}/orders';
}, 2000);
} else {
showMessage(response.message, 'error');
}
},
error: function() {
showMessage('秒杀失败,请重试', 'error');
}
});
// 防止重复点击
if (window.flashSaleInProgress) {
showMessage('操作进行中,请稍候...', 'warning');
return;
}
// 确认对话框
if (!confirm('确定要参与这个秒杀活动吗?\n\n注意每人限购一件确认后将立即抢购')) {
return;
}
// 找到按钮元素
const button = event.target.closest('button');
if (!button) return;
// 设置全局锁
window.flashSaleInProgress = true;
// 保存原始状态
const originalText = button.innerHTML;
const originalClass = button.className;
// 更新按钮状态
button.disabled = true;
button.className = 'btn btn-warning btn-sm w-100';
button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 抢购中...';
const startTime = Date.now();
$.ajax({
url: '${pageContext.request.contextPath}/api/flashsale/participate',
type: 'POST',
contentType: 'application/json',
timeout: 10000,
data: JSON.stringify({
flashSaleId: flashSaleId,
quantity: 1,
timestamp: startTime
}),
success: function (response) {
const duration = Date.now() - startTime;
if (response.success) {
// 成功状态
button.className = 'btn btn-success btn-sm w-100';
button.innerHTML = '<i class="fas fa-check"></i> 抢购成功!';
showMessage(`🎉 恭喜您!秒杀成功,订单已生成 (耗时: ${duration}ms)`, 'success');
// 刷新活动数据
setTimeout(() => {
loadActiveFlashSales();
}, 1000);
// 跳转到订单页面
setTimeout(() => {
window.location.href = '${pageContext.request.contextPath}/orders';
}, 3000);
} else {
// 失败状态
button.className = 'btn btn-danger btn-sm w-100';
button.innerHTML = '<i class="fas fa-times"></i> ' + (response.message || '抢购失败');
showMessage(response.message || '抢购失败,请重试', 'error');
// 恢复按钮状态
setTimeout(() => {
button.disabled = false;
button.className = originalClass;
button.innerHTML = originalText;
}, 2000);
}
},
error: function (xhr, status, error) {
let errorMessage = '网络异常,请重试';
if (status === 'timeout') {
errorMessage = '请求超时,请检查网络连接';
button.innerHTML = '<i class="fas fa-clock"></i> 请求超时';
} else if (xhr.status === 429) {
errorMessage = '请求过于频繁,请稍后再试';
button.innerHTML = '<i class="fas fa-ban"></i> 请求频繁';
} else {
button.innerHTML = '<i class="fas fa-exclamation-triangle"></i> 网络异常';
}
button.className = 'btn btn-danger btn-sm w-100';
showMessage(errorMessage, 'error');
// 恢复按钮状态
setTimeout(() => {
button.disabled = false;
button.className = originalClass;
button.innerHTML = originalText;
}, 3000);
},
complete: function () {
// 释放全局锁
setTimeout(() => {
window.flashSaleInProgress = false;
}, 1000);
}
});
</c:when>
<c:otherwise>
showMessage('请先登录', 'warning');
showMessage('请先登录后参与秒杀', 'warning');
setTimeout(() => {
window.location.href = '${pageContext.request.contextPath}/login';
}, 1000);
}, 1500);
</c:otherwise>
</c:choose>
}
@@ -467,6 +552,166 @@ function animateCounter(elementId, target, suffix = '') {
element.textContent = Math.floor(current).toLocaleString() + suffix;
}, 20);
}
// 倒计时函数
function countdown(endTime, elementId) {
const element = document.getElementById(elementId);
if (!element) return;
const timer = setInterval(() => {
const now = Date.now();
const timeLeft = endTime - now;
if (timeLeft <= 0) {
element.innerHTML = '<span class="text-muted">已结束</span>';
clearInterval(timer);
return;
}
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
element.innerHTML = `
<i class="fas fa-clock"></i>
${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}
`;
}, 1000);
}
// 显示消息
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);
}
// 更新购物车数量
function updateCartCount() {
$.get('${pageContext.request.contextPath}/api/cart/count')
.done(function (response) {
if (response.success) {
const cartBadge = document.querySelector('.cart-count');
if (cartBadge) {
const count = response.data.count || 0;
cartBadge.textContent = count;
cartBadge.style.display = count > 0 ? 'inline' : 'none';
}
}
});
}
</script>
<style>
/* 秒杀活动卡片样式 */
.card.border-danger {
border-width: 2px !important;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.card.border-danger:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(220, 53, 69, 0.3);
}
/* 热门商品卡片样式 */
.card:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
transition: all 0.3s ease;
}
/* 进度条样式 */
.progress {
background-color: rgba(220, 53, 69, 0.1);
}
/* 倒计时样式 */
.text-danger.fw-bold {
font-family: 'Courier New', monospace;
letter-spacing: 1px;
}
/* 折扣标签样式 */
.position-absolute.bg-warning {
font-weight: bold;
font-size: 0.75rem;
}
/* 商品图片样式 */
.card-img-top {
transition: transform 0.3s ease;
}
.card:hover .card-img-top {
transform: scale(1.05);
}
/* 按钮悬停效果 */
.btn {
transition: all 0.2s ease;
}
.btn:hover {
transform: translateY(-1px);
}
/* 响应式调整 */
@media (max-width: 768px) {
.card.border-danger:hover,
.card:hover {
transform: none;
}
.card:hover .card-img-top {
transform: none;
}
}
/* 加载动画 */
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.fa-spinner.fa-spin {
animation: spin 1s linear infinite;
}
/* 消息提示样式 */
.alert.position-fixed {
animation: slideInRight 0.3s ease-out;
}
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
</style>
<%@ include file="common/footer.jsp" %>