update
This commit is contained in:
parent
df3be587d8
commit
4ff8980268
@ -29,6 +29,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导航选项卡 -->
|
||||
<div class="tabs-section">
|
||||
<div class="tabs-nav">
|
||||
<button class="tab-btn active" onclick="showTab('orders')">订单管理</button>
|
||||
<button class="tab-btn" onclick="showTab('coupons')">优惠码管理</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单管理标签页 -->
|
||||
<div id="ordersTab" class="tab-content active">
|
||||
<div class="orders-section">
|
||||
<div class="section-header">
|
||||
<h2>订单列表</h2>
|
||||
@ -69,6 +79,87 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 优惠码管理标签页 -->
|
||||
<div id="couponsTab" class="tab-content">
|
||||
<div class="coupons-section">
|
||||
<div class="section-header">
|
||||
<h2>优惠码管理</h2>
|
||||
<button onclick="showCreateCouponModal()" class="btn-primary">+ 创建优惠码</button>
|
||||
</div>
|
||||
|
||||
<div class="coupons-table-container">
|
||||
<table class="coupons-table" id="couponsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>优惠码</th>
|
||||
<th>名称</th>
|
||||
<th>折扣</th>
|
||||
<th>类型</th>
|
||||
<th>使用情况</th>
|
||||
<th>状态</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="couponsTableBody">
|
||||
<tr>
|
||||
<td colspan="8" class="loading">加载中...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 创建优惠码模态框 -->
|
||||
<div id="createCouponModal" class="modal" style="display: none;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3>创建优惠码</h3>
|
||||
<span class="close" onclick="closeCreateCouponModal()">×</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="coupon-form">
|
||||
<div class="form-group">
|
||||
<label for="couponCode">优惠码:*</label>
|
||||
<input type="text" id="couponCode" placeholder="请输入优惠码(建议大写字母+数字)" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="couponName">名称:*</label>
|
||||
<input type="text" id="couponName" placeholder="请输入优惠码名称" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="discountType">折扣类型:*</label>
|
||||
<select id="discountType" onchange="onDiscountTypeChange()" required>
|
||||
<option value="percentage">百分比折扣</option>
|
||||
<option value="fixed">固定金额</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="discountValue">折扣值:*</label>
|
||||
<input type="number" id="discountValue" min="0" step="0.01" placeholder="请输入折扣值" required>
|
||||
<small id="discountHint" class="form-hint">输入百分比数值,如:10 表示10%折扣</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="isReusable" onchange="onReusableChange()">
|
||||
可重复使用
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" id="maxUsesGroup" style="display: none;">
|
||||
<label for="maxUses">最大使用次数:</label>
|
||||
<input type="number" id="maxUses" min="1" value="1" placeholder="请输入最大使用次数">
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button onclick="createCoupon()" class="btn-primary">创建优惠码</button>
|
||||
<button onclick="closeCreateCouponModal()" class="btn-secondary">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 发货模态框 -->
|
||||
<div id="shippingModal" class="modal" style="display: none;">
|
||||
|
||||
@ -46,6 +46,230 @@ header p {
|
||||
|
||||
/* 管理员导航 - 已移除 */
|
||||
|
||||
/* 选项卡导航 */
|
||||
.tabs-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.tabs-nav {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
border-bottom: 2px solid #333;
|
||||
}
|
||||
|
||||
.tab-btn {
|
||||
padding: 12px 25px;
|
||||
background: linear-gradient(145deg, #2d2d2d, #3d3d3d);
|
||||
color: #b0b0b0;
|
||||
border: none;
|
||||
border-radius: 10px 10px 0 0;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.tab-btn:hover {
|
||||
background: linear-gradient(145deg, #3d3d3d, #4d4d4d);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.tab-btn.active {
|
||||
background: linear-gradient(145deg, #1a1a1a, #2d2d2d);
|
||||
color: #ffd700;
|
||||
border-bottom: 3px solid #ffd700;
|
||||
box-shadow: 0 5px 15px rgba(255, 215, 0, 0.2);
|
||||
}
|
||||
|
||||
/* 选项卡内容 */
|
||||
.tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 优惠码表格和订单表格共用样式 */
|
||||
.coupons-section,
|
||||
.orders-section {
|
||||
background: linear-gradient(145deg, #1a1a1a, #2d2d2d);
|
||||
border-radius: 15px;
|
||||
padding: 30px;
|
||||
border: 1px solid #444;
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.coupons-table-container,
|
||||
.orders-table-container {
|
||||
overflow-x: auto;
|
||||
background: #0a0a0a;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.coupons-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.coupons-table th {
|
||||
background: linear-gradient(145deg, #2d2d2d, #3d3d3d);
|
||||
color: #ffd700;
|
||||
padding: 15px 12px;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
border-bottom: 2px solid #444;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.coupons-table td {
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #333;
|
||||
color: #e0e0e0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.coupons-table tbody tr {
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.coupons-table tbody tr:hover {
|
||||
background-color: rgba(255, 215, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 优惠码状态标签 */
|
||||
.coupon-active {
|
||||
background: rgba(40, 167, 69, 0.2);
|
||||
color: #28a745;
|
||||
border: 1px solid #28a745;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.coupon-inactive {
|
||||
background: rgba(220, 53, 69, 0.2);
|
||||
color: #dc3545;
|
||||
border: 1px solid #dc3545;
|
||||
padding: 4px 10px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 优惠码类型标签 */
|
||||
.coupon-type {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 8px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.coupon-percentage {
|
||||
background: rgba(0, 123, 255, 0.2);
|
||||
color: #007bff;
|
||||
border: 1px solid #007bff;
|
||||
}
|
||||
|
||||
.coupon-fixed {
|
||||
background: rgba(255, 193, 7, 0.2);
|
||||
color: #ffc107;
|
||||
border: 1px solid #ffc107;
|
||||
}
|
||||
|
||||
/* 优惠码表单样式 */
|
||||
.coupon-form .form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.coupon-form label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.coupon-form input,
|
||||
.coupon-form select {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: #0a0a0a;
|
||||
border: 2px solid #444;
|
||||
border-radius: 8px;
|
||||
color: #e0e0e0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.coupon-form input:focus,
|
||||
.coupon-form select:focus {
|
||||
outline: none;
|
||||
border-color: #ffd700;
|
||||
box-shadow: 0 0 15px rgba(255, 215, 0, 0.2);
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
color: #888;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 复选框样式 */
|
||||
.coupon-form input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin-right: 8px;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.coupon-form label:has(input[type="checkbox"]) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 按钮样式增强 */
|
||||
.btn-toggle {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-enable {
|
||||
background: linear-gradient(135deg, #28a745, #20c997);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-enable:hover {
|
||||
background: linear-gradient(135deg, #20c997, #17a2b8);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-disable {
|
||||
background: linear-gradient(135deg, #6c757d, #5a6268);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-disable:hover {
|
||||
background: linear-gradient(135deg, #5a6268, #495057);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* 管理员导航 - 已移除 */
|
||||
|
||||
/* 统计卡片 */
|
||||
.stats-section {
|
||||
display: grid;
|
||||
|
||||
@ -293,6 +293,11 @@ main {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.coupon-discount-line {
|
||||
color: #007bff !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.subtotal-line {
|
||||
color: #ffc107;
|
||||
text-decoration: line-through;
|
||||
@ -312,6 +317,96 @@ main {
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
/* 优惠码输入区域 */
|
||||
.coupon-section {
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(145deg, #0f1419, #1a2332);
|
||||
border-radius: 12px;
|
||||
border: 1px solid #2c3e50;
|
||||
}
|
||||
|
||||
.coupon-input-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.coupon-input-group input {
|
||||
flex: 1;
|
||||
padding: 12px 15px;
|
||||
border: 2px solid #444;
|
||||
border-radius: 8px;
|
||||
background: #0a0a0a;
|
||||
color: #e0e0e0;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.coupon-input-group input:focus {
|
||||
outline: none;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 0 15px rgba(0, 123, 255, 0.2);
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
.coupon-input-group input::placeholder {
|
||||
color: #666;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.coupon-input-group button {
|
||||
padding: 12px 20px;
|
||||
background: linear-gradient(135deg, #007bff, #0056b3);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.coupon-input-group button:hover {
|
||||
background: linear-gradient(135deg, #0056b3, #004085);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(0, 123, 255, 0.3);
|
||||
}
|
||||
|
||||
.coupon-input-group button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.coupon-message {
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.coupon-message.success {
|
||||
background: rgba(40, 167, 69, 0.1);
|
||||
color: #28a745;
|
||||
border: 1px solid #28a745;
|
||||
}
|
||||
|
||||
.coupon-message.error {
|
||||
background: rgba(220, 53, 69, 0.1);
|
||||
color: #dc3545;
|
||||
border: 1px solid #dc3545;
|
||||
}
|
||||
|
||||
.coupon-message.info {
|
||||
background: rgba(0, 123, 255, 0.1);
|
||||
color: #007bff;
|
||||
border: 1px solid #007bff;
|
||||
}
|
||||
|
||||
/* 优惠提示样式 */
|
||||
.discount-tips {
|
||||
margin-top: 20px;
|
||||
|
||||
@ -61,6 +61,10 @@
|
||||
<span>优惠:</span>
|
||||
<span id="discount-text">$0.00 USDT (无优惠)</span>
|
||||
</div>
|
||||
<div class="price-line coupon-discount-line" id="coupon-discount-line" style="display: none;">
|
||||
<span>优惠码:</span>
|
||||
<span id="coupon-discount-text">$0.00 USDT</span>
|
||||
</div>
|
||||
<div class="price-line subtotal-line" id="subtotal-line">
|
||||
<span>小计:</span>
|
||||
<span id="subtotal-price">$99.99 USDT</span>
|
||||
@ -70,6 +74,15 @@
|
||||
<span id="total-price">$99.99 USDT</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 优惠码输入 -->
|
||||
<div class="coupon-section">
|
||||
<div class="coupon-input-group">
|
||||
<input type="text" id="coupon-code" placeholder="输入优惠码(可选)" maxlength="20">
|
||||
<button type="button" id="apply-coupon-btn" onclick="applyCoupon()">使用</button>
|
||||
</div>
|
||||
<div id="coupon-message" class="coupon-message" style="display: none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 配送信息分割线 -->
|
||||
|
||||
@ -1,19 +1,47 @@
|
||||
// 订单管理页面脚本
|
||||
let currentOrders = [];
|
||||
let currentCoupons = [];
|
||||
let currentOrderId = null;
|
||||
|
||||
// DOM 元素
|
||||
const ordersTableBody = document.getElementById('ordersTableBody');
|
||||
const couponsTableBody = document.getElementById('couponsTableBody');
|
||||
const searchInput = document.getElementById('searchOrder');
|
||||
const statusFilter = document.getElementById('statusFilter');
|
||||
const orderDetailModal = document.getElementById('orderDetailModal');
|
||||
const shippingModal = document.getElementById('shippingModal');
|
||||
const createCouponModal = document.getElementById('createCouponModal');
|
||||
|
||||
// 页面加载时初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadOrders();
|
||||
loadCoupons();
|
||||
});
|
||||
|
||||
// 选项卡切换功能
|
||||
function showTab(tabName) {
|
||||
// 隐藏所有选项卡内容
|
||||
const tabs = document.querySelectorAll('.tab-content');
|
||||
tabs.forEach(tab => tab.classList.remove('active'));
|
||||
|
||||
// 移除所有按钮的活动状态
|
||||
const buttons = document.querySelectorAll('.tab-btn');
|
||||
buttons.forEach(btn => btn.classList.remove('active'));
|
||||
|
||||
// 显示选中的选项卡内容
|
||||
document.getElementById(tabName + 'Tab').classList.add('active');
|
||||
|
||||
// 激活相应按钮
|
||||
event.target.classList.add('active');
|
||||
|
||||
// 根据选项卡加载数据
|
||||
if (tabName === 'coupons') {
|
||||
loadCoupons();
|
||||
} else if (tabName === 'orders') {
|
||||
loadOrders();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载订单列表
|
||||
async function loadOrders(search = '', status = '') {
|
||||
try {
|
||||
@ -430,6 +458,258 @@ function formatDate(dateString) {
|
||||
});
|
||||
}
|
||||
|
||||
// 加载优惠码列表
|
||||
async function loadCoupons() {
|
||||
try {
|
||||
showCouponsLoading();
|
||||
|
||||
const response = await fetch('/api/admin/coupons');
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
currentCoupons = data.coupons;
|
||||
updateCouponsTable(data.coupons);
|
||||
} else {
|
||||
throw new Error(data.error || '加载优惠码失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Load coupons error:', error);
|
||||
showCouponsError('加载优惠码失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新优惠码表格
|
||||
function updateCouponsTable(coupons) {
|
||||
if (!coupons || coupons.length === 0) {
|
||||
couponsTableBody.innerHTML = '<tr><td colspan="8" class="loading">暂无优惠码数据</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
couponsTableBody.innerHTML = coupons.map(coupon => `
|
||||
<tr>
|
||||
<td>
|
||||
<strong style="font-family: monospace; color: #ffd700;">${coupon.code}</strong>
|
||||
</td>
|
||||
<td>${coupon.name}</td>
|
||||
<td>
|
||||
<strong style="color: #28a745;">
|
||||
${coupon.discount_type === 'percentage' ? coupon.discount_value + '%' : '$' + coupon.discount_value.toFixed(2)}
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
<span class="coupon-type coupon-${coupon.discount_type}">
|
||||
${coupon.discount_type === 'percentage' ? '百分比' : '固定金额'}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div style="font-size: 13px;">
|
||||
已使用: <strong>${coupon.used_count}</strong> / ${coupon.max_uses === 999999 ? '无限' : coupon.max_uses}<br>
|
||||
<small style="color: #888;">${coupon.is_reusable ? '可重复使用' : '一次性使用'}</small>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="status-badge ${coupon.is_active ? 'coupon-active' : 'coupon-inactive'}">
|
||||
${coupon.is_active ? '启用' : '禁用'}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
${formatDate(coupon.created_at)}
|
||||
</td>
|
||||
<td>
|
||||
<button class="action-btn ${coupon.is_active ? 'btn-disable' : 'btn-enable'}"
|
||||
onclick="toggleCouponStatus(${coupon.id}, ${!coupon.is_active})"
|
||||
title="${coupon.is_active ? '禁用' : '启用'}优惠码">
|
||||
${coupon.is_active ? '禁用' : '启用'}
|
||||
</button>
|
||||
<button class="action-btn btn-delete" onclick="confirmDeleteCoupon(${coupon.id})" title="删除优惠码">
|
||||
删除
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 显示创建优惠码模态框
|
||||
function showCreateCouponModal() {
|
||||
// 清空表单
|
||||
document.getElementById('couponCode').value = '';
|
||||
document.getElementById('couponName').value = '';
|
||||
document.getElementById('discountType').value = 'percentage';
|
||||
document.getElementById('discountValue').value = '';
|
||||
document.getElementById('isReusable').checked = false;
|
||||
document.getElementById('maxUses').value = '1';
|
||||
document.getElementById('maxUsesGroup').style.display = 'none';
|
||||
onDiscountTypeChange(); // 更新提示文本
|
||||
|
||||
createCouponModal.style.display = 'flex';
|
||||
document.getElementById('couponCode').focus();
|
||||
}
|
||||
|
||||
// 关闭创建优惠码模态框
|
||||
function closeCreateCouponModal() {
|
||||
createCouponModal.style.display = 'none';
|
||||
}
|
||||
|
||||
// 折扣类型变化处理
|
||||
function onDiscountTypeChange() {
|
||||
const discountType = document.getElementById('discountType').value;
|
||||
const hint = document.getElementById('discountHint');
|
||||
|
||||
if (discountType === 'percentage') {
|
||||
hint.textContent = '输入百分比数值,如:10 表示10%折扣';
|
||||
} else {
|
||||
hint.textContent = '输入固定金额,如:5 表示减5美元';
|
||||
}
|
||||
}
|
||||
|
||||
// 可重复使用变化处理
|
||||
function onReusableChange() {
|
||||
const isReusable = document.getElementById('isReusable').checked;
|
||||
const maxUsesGroup = document.getElementById('maxUsesGroup');
|
||||
|
||||
if (isReusable) {
|
||||
maxUsesGroup.style.display = 'block';
|
||||
document.getElementById('maxUses').value = '10'; // 默认10次
|
||||
} else {
|
||||
maxUsesGroup.style.display = 'none';
|
||||
document.getElementById('maxUses').value = '1';
|
||||
}
|
||||
}
|
||||
|
||||
// 创建优惠码
|
||||
async function createCoupon() {
|
||||
const code = document.getElementById('couponCode').value.trim();
|
||||
const name = document.getElementById('couponName').value.trim();
|
||||
const discountType = document.getElementById('discountType').value;
|
||||
const discountValue = parseFloat(document.getElementById('discountValue').value);
|
||||
const isReusable = document.getElementById('isReusable').checked;
|
||||
const maxUses = parseInt(document.getElementById('maxUses').value);
|
||||
|
||||
// 验证表单
|
||||
if (!code || !name || !discountValue) {
|
||||
alert('请填写所有必填字段');
|
||||
return;
|
||||
}
|
||||
|
||||
if (discountValue <= 0) {
|
||||
alert('折扣值必须大于0');
|
||||
return;
|
||||
}
|
||||
|
||||
if (discountType === 'percentage' && discountValue > 100) {
|
||||
alert('百分比折扣不能超过100%');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isReusable && (!maxUses || maxUses < 1)) {
|
||||
alert('最大使用次数必须大于0');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/admin/coupons', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code: code,
|
||||
name: name,
|
||||
discount_type: discountType,
|
||||
discount_value: discountValue,
|
||||
is_reusable: isReusable,
|
||||
max_uses: maxUses
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert('优惠码创建成功!');
|
||||
closeCreateCouponModal();
|
||||
loadCoupons(); // 重新加载优惠码列表
|
||||
} else {
|
||||
throw new Error(result.error || '创建优惠码失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Create coupon error:', error);
|
||||
alert('创建优惠码失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 切换优惠码状态
|
||||
async function toggleCouponStatus(couponId, isActive) {
|
||||
try {
|
||||
const response = await fetch(`/api/admin/coupons/${couponId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
is_active: isActive
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert(`优惠码已${isActive ? '启用' : '禁用'}!`);
|
||||
loadCoupons(); // 重新加载优惠码列表
|
||||
} else {
|
||||
throw new Error(result.error || '更新优惠码状态失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Toggle coupon status error:', error);
|
||||
alert('更新优惠码状态失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 确认删除优惠码
|
||||
function confirmDeleteCoupon(couponId) {
|
||||
const coupon = currentCoupons.find(c => c.id === couponId);
|
||||
if (!coupon) {
|
||||
alert('优惠码未找到');
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmMessage = `确认删除以下优惠码吗?\n\n优惠码:${coupon.code}\n名称:${coupon.name}\n已使用次数:${coupon.used_count}\n\n⚠️ 此操作不可撤销!`;
|
||||
|
||||
if (confirm(confirmMessage)) {
|
||||
deleteCoupon(couponId);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除优惠码
|
||||
async function deleteCoupon(couponId) {
|
||||
try {
|
||||
const response = await fetch(`/api/admin/coupons/${couponId}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
alert('优惠码已成功删除!');
|
||||
loadCoupons(); // 重新加载优惠码列表
|
||||
} else {
|
||||
throw new Error(result.error || '删除优惠码失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Delete coupon error:', error);
|
||||
alert('删除优惠码失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 显示优惠码加载状态
|
||||
function showCouponsLoading() {
|
||||
couponsTableBody.innerHTML = '<tr><td colspan="8" class="loading">加载中...</td></tr>';
|
||||
}
|
||||
|
||||
// 显示优惠码错误信息
|
||||
function showCouponsError(message) {
|
||||
couponsTableBody.innerHTML = `<tr><td colspan="8" class="loading" style="color: #ff4757;">错误: ${message}</td></tr>`;
|
||||
}
|
||||
|
||||
// 确认删除订单
|
||||
function confirmDeleteOrder(orderId) {
|
||||
const order = currentOrders.find(o => o.order_id === orderId);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// 全局变量
|
||||
let currentProduct = null;
|
||||
let currentOrder = null;
|
||||
let appliedCoupon = null; // 当前应用的优惠码
|
||||
|
||||
// DOM 元素
|
||||
const productPrice = document.getElementById('product-price');
|
||||
@ -99,6 +100,24 @@ function setupEventListeners() {
|
||||
}
|
||||
});
|
||||
|
||||
// 优惠码输入监听
|
||||
document.getElementById('coupon-code').addEventListener('input', function(e) {
|
||||
// 自动转换为大写
|
||||
e.target.value = e.target.value.toUpperCase();
|
||||
// 清除已应用的优惠码状态
|
||||
if (appliedCoupon) {
|
||||
clearAppliedCoupon();
|
||||
}
|
||||
});
|
||||
|
||||
// 优惠码回车键应用
|
||||
document.getElementById('coupon-code').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
applyCoupon();
|
||||
}
|
||||
});
|
||||
|
||||
// 支付状态检查
|
||||
document.getElementById('check-status-btn').addEventListener('click', checkPaymentStatus);
|
||||
|
||||
@ -118,41 +137,61 @@ function updateTotalPrice() {
|
||||
const unitPrice = currentProduct.price;
|
||||
const originalTotal = unitPrice * quantity;
|
||||
|
||||
// 计算折扣
|
||||
let discount = 0;
|
||||
let discountText = '';
|
||||
let finalTotal = originalTotal;
|
||||
// 计算数量折扣
|
||||
let quantityDiscount = 0;
|
||||
let quantityDiscountText = '';
|
||||
let afterQuantityDiscount = originalTotal;
|
||||
|
||||
if (quantity >= 5) {
|
||||
discount = 0.1; // 9折,优惠10%
|
||||
discountText = '5个及以上 9折优惠';
|
||||
finalTotal = originalTotal * 0.9;
|
||||
quantityDiscount = originalTotal * 0.1; // 9折,优惠10%
|
||||
quantityDiscountText = '5个及以上 9折优惠';
|
||||
afterQuantityDiscount = originalTotal * 0.9;
|
||||
} else if (quantity >= 2) {
|
||||
discount = 0.05; // 9.5折,优惠5%
|
||||
discountText = '2个 9.5折优惠';
|
||||
finalTotal = originalTotal * 0.95;
|
||||
quantityDiscount = originalTotal * 0.05; // 9.5折,优惠5%
|
||||
quantityDiscountText = '2个 9.5折优惠';
|
||||
afterQuantityDiscount = originalTotal * 0.95;
|
||||
}
|
||||
|
||||
// 计算优惠码折扣
|
||||
let couponDiscount = 0;
|
||||
let finalTotal = afterQuantityDiscount;
|
||||
|
||||
if (appliedCoupon) {
|
||||
if (appliedCoupon.discount_type === 'percentage') {
|
||||
couponDiscount = afterQuantityDiscount * (appliedCoupon.discount_value / 100);
|
||||
} else if (appliedCoupon.discount_type === 'fixed') {
|
||||
couponDiscount = Math.min(appliedCoupon.discount_value, afterQuantityDiscount);
|
||||
}
|
||||
finalTotal = afterQuantityDiscount - couponDiscount;
|
||||
}
|
||||
|
||||
// 更新显示
|
||||
const discountLine = document.getElementById('discount-line');
|
||||
const subtotalLine = document.getElementById('subtotal-line');
|
||||
const couponDiscountLine = document.getElementById('coupon-discount-line');
|
||||
const discountTextElement = document.getElementById('discount-text');
|
||||
const subtotalPriceElement = document.getElementById('subtotal-price');
|
||||
const couponDiscountTextElement = document.getElementById('coupon-discount-text');
|
||||
|
||||
// 始终显示优惠和小计信息
|
||||
if (discount > 0) {
|
||||
// 显示优惠信息
|
||||
const discountAmount = originalTotal - finalTotal;
|
||||
discountTextElement.textContent = `-$${discountAmount.toFixed(2)} USDT (${discountText})`;
|
||||
// 数量折扣显示
|
||||
if (quantityDiscount > 0) {
|
||||
discountTextElement.textContent = `-$${quantityDiscount.toFixed(2)} USDT (${quantityDiscountText})`;
|
||||
subtotalPriceElement.textContent = `$${originalTotal.toFixed(2)} USDT`;
|
||||
discountLine.classList.remove('no-discount');
|
||||
} else {
|
||||
// 显示无优惠
|
||||
discountTextElement.textContent = `$0.00 USDT (无优惠)`;
|
||||
subtotalPriceElement.textContent = `$${originalTotal.toFixed(2)} USDT`;
|
||||
discountLine.classList.add('no-discount');
|
||||
}
|
||||
|
||||
// 优惠码折扣显示
|
||||
if (appliedCoupon && couponDiscount > 0) {
|
||||
couponDiscountTextElement.textContent = `-$${couponDiscount.toFixed(2)} USDT (${appliedCoupon.name})`;
|
||||
couponDiscountLine.style.display = 'flex';
|
||||
} else {
|
||||
couponDiscountLine.style.display = 'none';
|
||||
}
|
||||
|
||||
discountLine.style.display = 'flex';
|
||||
subtotalLine.style.display = 'flex';
|
||||
|
||||
@ -221,6 +260,87 @@ function validatePhoneField(phoneInput) {
|
||||
}
|
||||
}
|
||||
|
||||
// 应用优惠码
|
||||
async function applyCoupon() {
|
||||
const couponCode = document.getElementById('coupon-code').value.trim();
|
||||
const applyBtn = document.getElementById('apply-coupon-btn');
|
||||
const messageEl = document.getElementById('coupon-message');
|
||||
|
||||
if (!couponCode) {
|
||||
showCouponMessage('请输入优惠码', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// 禁用按钮
|
||||
applyBtn.disabled = true;
|
||||
applyBtn.textContent = '验证中...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/coupons/validate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ code: couponCode })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.valid) {
|
||||
// 优惠码有效,应用优惠
|
||||
appliedCoupon = result.coupon;
|
||||
updateTotalPrice();
|
||||
|
||||
// 更新UI状态
|
||||
document.getElementById('coupon-code').readOnly = true;
|
||||
applyBtn.textContent = '移除';
|
||||
applyBtn.onclick = clearAppliedCoupon;
|
||||
|
||||
const discountText = result.coupon.discount_type === 'percentage'
|
||||
? `${result.coupon.discount_value}%折扣`
|
||||
: `$${result.coupon.discount_value}减免`;
|
||||
showCouponMessage(`✓ 优惠码"${result.coupon.name}"已应用 (${discountText})`, 'success');
|
||||
} else {
|
||||
showCouponMessage(result.message || '优惠码无效', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Apply coupon error:', error);
|
||||
showCouponMessage('验证优惠码失败,请稍后重试', 'error');
|
||||
} finally {
|
||||
if (!appliedCoupon) {
|
||||
applyBtn.disabled = false;
|
||||
applyBtn.textContent = '使用';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清除已应用的优惠码
|
||||
function clearAppliedCoupon() {
|
||||
appliedCoupon = null;
|
||||
updateTotalPrice();
|
||||
|
||||
// 重置UI状态
|
||||
document.getElementById('coupon-code').readOnly = false;
|
||||
document.getElementById('coupon-code').value = '';
|
||||
const applyBtn = document.getElementById('apply-coupon-btn');
|
||||
applyBtn.disabled = false;
|
||||
applyBtn.textContent = '使用';
|
||||
applyBtn.onclick = applyCoupon;
|
||||
|
||||
showCouponMessage('优惠码已移除', 'info');
|
||||
setTimeout(() => {
|
||||
document.getElementById('coupon-message').style.display = 'none';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 显示优惠码消息
|
||||
function showCouponMessage(message, type = 'info') {
|
||||
const messageEl = document.getElementById('coupon-message');
|
||||
messageEl.textContent = message;
|
||||
messageEl.className = `coupon-message ${type}`;
|
||||
messageEl.style.display = 'block';
|
||||
}
|
||||
|
||||
// 验证数量
|
||||
function validateQuantity() {
|
||||
const value = parseInt(quantityInput.value);
|
||||
@ -264,7 +384,8 @@ async function handleOrderSubmit(e) {
|
||||
customer_name: formData.get('customer_name').trim(),
|
||||
customer_email: formData.get('customer_email').trim(),
|
||||
customer_phone: formData.get('customer_phone').trim(),
|
||||
shipping_address: formData.get('shipping_address').trim()
|
||||
shipping_address: formData.get('shipping_address').trim(),
|
||||
coupon_code: appliedCoupon ? appliedCoupon.code : null
|
||||
};
|
||||
|
||||
// 验证必填字段
|
||||
@ -608,7 +729,8 @@ async function handleShippingOrderSubmit(e) {
|
||||
customer_name: formData.get('customer_name').trim(),
|
||||
customer_email: formData.get('customer_email').trim(),
|
||||
customer_phone: formData.get('customer_phone').trim(),
|
||||
shipping_address: formData.get('shipping_address').trim()
|
||||
shipping_address: formData.get('shipping_address').trim(),
|
||||
coupon_code: appliedCoupon ? appliedCoupon.code : null
|
||||
};
|
||||
|
||||
// 验证必填字段
|
||||
|
||||
274
server.js
274
server.js
@ -36,9 +36,36 @@ db.serialize(() => {
|
||||
shipping_status TEXT DEFAULT 'pending',
|
||||
tracking_number TEXT,
|
||||
shipping_notes TEXT,
|
||||
coupon_code TEXT,
|
||||
coupon_discount REAL DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// 创建优惠码表
|
||||
db.run(`CREATE TABLE IF NOT EXISTS coupons (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
discount_type TEXT NOT NULL, -- 'percentage' 或 'fixed'
|
||||
discount_value REAL NOT NULL,
|
||||
is_reusable INTEGER DEFAULT 0, -- 0:一次性 1:可重复使用
|
||||
used_count INTEGER DEFAULT 0,
|
||||
max_uses INTEGER DEFAULT 1,
|
||||
is_active INTEGER DEFAULT 1, -- 0:禁用 1:启用
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// 创建优惠码使用记录表
|
||||
db.run(`CREATE TABLE IF NOT EXISTS coupon_uses (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
coupon_id INTEGER NOT NULL,
|
||||
order_id TEXT NOT NULL,
|
||||
used_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (coupon_id) REFERENCES coupons(id),
|
||||
FOREIGN KEY (order_id) REFERENCES orders(order_id)
|
||||
)`);
|
||||
});
|
||||
|
||||
// UPay 配置
|
||||
@ -85,7 +112,8 @@ app.post('/api/orders', async (req, res) => {
|
||||
customer_name,
|
||||
customer_email,
|
||||
customer_phone,
|
||||
shipping_address
|
||||
shipping_address,
|
||||
coupon_code
|
||||
} = req.body;
|
||||
|
||||
// 验证产品
|
||||
@ -99,12 +127,53 @@ app.post('/api/orders', async (req, res) => {
|
||||
const expectedUnitPrice = product.price;
|
||||
const originalTotal = expectedUnitPrice * quantity;
|
||||
let expectedTotal = originalTotal;
|
||||
let quantityDiscount = 0;
|
||||
let couponDiscount = 0;
|
||||
let appliedCoupon = null;
|
||||
|
||||
// 服务器端折扣计算验证
|
||||
// 数量折扣计算
|
||||
if (quantity >= 5) {
|
||||
expectedTotal = originalTotal * 0.9; // 9折
|
||||
quantityDiscount = originalTotal * 0.1; // 9折,优惠10%
|
||||
expectedTotal = originalTotal * 0.9;
|
||||
} else if (quantity >= 2) {
|
||||
expectedTotal = originalTotal * 0.95; // 9.5折
|
||||
quantityDiscount = originalTotal * 0.05; // 9.5折,优惠5%
|
||||
expectedTotal = originalTotal * 0.95;
|
||||
}
|
||||
|
||||
// 处理优惠码
|
||||
if (coupon_code) {
|
||||
const coupon = await new Promise((resolve, reject) => {
|
||||
db.get(`
|
||||
SELECT * FROM coupons
|
||||
WHERE code = ? AND is_active = 1
|
||||
`, [coupon_code.toUpperCase()], (err, row) => {
|
||||
if (err) reject(err);
|
||||
else resolve(row);
|
||||
});
|
||||
});
|
||||
|
||||
if (coupon) {
|
||||
// 检查使用次数限制
|
||||
if (!coupon.is_reusable && coupon.used_count >= coupon.max_uses) {
|
||||
return res.status(400).json({ error: '优惠码已被使用' });
|
||||
}
|
||||
|
||||
if (coupon.is_reusable && coupon.used_count >= coupon.max_uses) {
|
||||
return res.status(400).json({ error: '优惠码使用次数已达上限' });
|
||||
}
|
||||
|
||||
// 计算优惠码折扣(基于数量折扣后的价格)
|
||||
if (coupon.discount_type === 'percentage') {
|
||||
couponDiscount = expectedTotal * (coupon.discount_value / 100);
|
||||
} else if (coupon.discount_type === 'fixed') {
|
||||
couponDiscount = Math.min(coupon.discount_value, expectedTotal);
|
||||
}
|
||||
|
||||
expectedTotal -= couponDiscount;
|
||||
appliedCoupon = coupon;
|
||||
} else {
|
||||
return res.status(400).json({ error: '优惠码不存在或已失效' });
|
||||
}
|
||||
}
|
||||
|
||||
// 价格验证(允许小数点误差)
|
||||
@ -117,23 +186,41 @@ app.post('/api/orders', async (req, res) => {
|
||||
// 保存订单到数据库
|
||||
const stmt = db.prepare(`
|
||||
INSERT INTO orders (order_id, product_name, quantity, unit_price, total_amount,
|
||||
customer_name, customer_email, customer_phone, shipping_address)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
customer_name, customer_email, customer_phone, shipping_address,
|
||||
coupon_code, coupon_discount)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
stmt.run([
|
||||
order_id, product.name, quantity, expectedTotal / quantity, expectedTotal,
|
||||
customer_name, customer_email, customer_phone, shipping_address
|
||||
customer_name, customer_email, customer_phone, shipping_address,
|
||||
appliedCoupon ? appliedCoupon.code : null,
|
||||
couponDiscount
|
||||
], function(err) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return res.status(500).json({ error: '订单创建失败' });
|
||||
}
|
||||
|
||||
// 如果使用了优惠码,更新使用次数和记录
|
||||
if (appliedCoupon) {
|
||||
db.run(`
|
||||
UPDATE coupons
|
||||
SET used_count = used_count + 1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
`, [appliedCoupon.id]);
|
||||
|
||||
db.run(`
|
||||
INSERT INTO coupon_uses (coupon_id, order_id)
|
||||
VALUES (?, ?)
|
||||
`, [appliedCoupon.id, order_id]);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
order_id,
|
||||
total_amount: expectedTotal,
|
||||
coupon_discount: couponDiscount,
|
||||
message: '订单创建成功'
|
||||
});
|
||||
});
|
||||
@ -411,6 +498,179 @@ app.put('/api/admin/orders/:order_id/shipping', (req, res) => {
|
||||
);
|
||||
});
|
||||
|
||||
// 验证优惠码
|
||||
app.post('/api/coupons/validate', (req, res) => {
|
||||
const { code } = req.body;
|
||||
|
||||
if (!code) {
|
||||
return res.status(400).json({ error: '请输入优惠码' });
|
||||
}
|
||||
|
||||
db.get(`
|
||||
SELECT * FROM coupons
|
||||
WHERE code = ? AND is_active = 1
|
||||
`, [code], (err, coupon) => {
|
||||
if (err) {
|
||||
console.error('Query coupon error:', err);
|
||||
return res.status(500).json({ error: '查询优惠码失败' });
|
||||
}
|
||||
|
||||
if (!coupon) {
|
||||
return res.json({ valid: false, message: '优惠码不存在或已失效' });
|
||||
}
|
||||
|
||||
// 检查使用次数限制
|
||||
if (!coupon.is_reusable && coupon.used_count >= coupon.max_uses) {
|
||||
return res.json({ valid: false, message: '优惠码已被使用' });
|
||||
}
|
||||
|
||||
if (coupon.is_reusable && coupon.used_count >= coupon.max_uses) {
|
||||
return res.json({ valid: false, message: '优惠码使用次数已达上限' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
valid: true,
|
||||
coupon: {
|
||||
id: coupon.id,
|
||||
code: coupon.code,
|
||||
name: coupon.name,
|
||||
discount_type: coupon.discount_type,
|
||||
discount_value: coupon.discount_value
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 获取所有优惠码(管理员)
|
||||
app.get('/api/admin/coupons', (req, res) => {
|
||||
const { limit = 50, offset = 0 } = req.query;
|
||||
|
||||
db.all(`
|
||||
SELECT * FROM coupons
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`, [parseInt(limit), parseInt(offset)], (err, coupons) => {
|
||||
if (err) {
|
||||
console.error('Query coupons error:', err);
|
||||
return res.status(500).json({ error: '查询优惠码失败' });
|
||||
}
|
||||
|
||||
res.json({ coupons });
|
||||
});
|
||||
});
|
||||
|
||||
// 创建优惠码(管理员)
|
||||
app.post('/api/admin/coupons', (req, res) => {
|
||||
const {
|
||||
code,
|
||||
name,
|
||||
discount_type,
|
||||
discount_value,
|
||||
is_reusable,
|
||||
max_uses
|
||||
} = req.body;
|
||||
|
||||
// 验证必填字段
|
||||
if (!code || !name || !discount_type || discount_value === undefined) {
|
||||
return res.status(400).json({ error: '请填写所有必填字段' });
|
||||
}
|
||||
|
||||
// 验证折扣类型
|
||||
if (!['percentage', 'fixed'].includes(discount_type)) {
|
||||
return res.status(400).json({ error: '无效的折扣类型' });
|
||||
}
|
||||
|
||||
// 验证折扣值
|
||||
if (discount_value <= 0) {
|
||||
return res.status(400).json({ error: '折扣值必须大于0' });
|
||||
}
|
||||
|
||||
if (discount_type === 'percentage' && discount_value > 100) {
|
||||
return res.status(400).json({ error: '百分比折扣不能超过100%' });
|
||||
}
|
||||
|
||||
const stmt = db.prepare(`
|
||||
INSERT INTO coupons (code, name, discount_type, discount_value, is_reusable, max_uses)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
stmt.run([
|
||||
code.toUpperCase(),
|
||||
name,
|
||||
discount_type,
|
||||
discount_value,
|
||||
is_reusable ? 1 : 0,
|
||||
max_uses || 1
|
||||
], function(err) {
|
||||
if (err) {
|
||||
console.error('Create coupon error:', err);
|
||||
if (err.message.includes('UNIQUE constraint failed')) {
|
||||
return res.status(400).json({ error: '优惠码已存在' });
|
||||
}
|
||||
return res.status(500).json({ error: '创建优惠码失败' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '优惠码创建成功',
|
||||
coupon_id: this.lastID
|
||||
});
|
||||
});
|
||||
|
||||
stmt.finalize();
|
||||
});
|
||||
|
||||
// 更新优惠码状态(管理员)
|
||||
app.put('/api/admin/coupons/:id', (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { is_active } = req.body;
|
||||
|
||||
if (is_active === undefined) {
|
||||
return res.status(400).json({ error: '请提供状态参数' });
|
||||
}
|
||||
|
||||
db.run(`
|
||||
UPDATE coupons
|
||||
SET is_active = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
`, [is_active ? 1 : 0, id], function(err) {
|
||||
if (err) {
|
||||
console.error('Update coupon error:', err);
|
||||
return res.status(500).json({ error: '更新优惠码失败' });
|
||||
}
|
||||
|
||||
if (this.changes === 0) {
|
||||
return res.status(404).json({ error: '优惠码未找到' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '优惠码状态更新成功'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 删除优惠码(管理员)
|
||||
app.delete('/api/admin/coupons/:id', (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
db.run('DELETE FROM coupons WHERE id = ?', [id], function(err) {
|
||||
if (err) {
|
||||
console.error('Delete coupon error:', err);
|
||||
return res.status(500).json({ error: '删除优惠码失败' });
|
||||
}
|
||||
|
||||
if (this.changes === 0) {
|
||||
return res.status(404).json({ error: '优惠码未找到' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '优惠码已删除'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 删除订单(管理员)
|
||||
app.delete('/api/admin/orders/:order_id', (req, res) => {
|
||||
const { order_id } = req.params;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user