```
feat(admin): 添加订单详情页面和API接口 - 新增 `/admin/orders/<int:order_id>` 路由用于显示订单详情页面 - 在管理后台的订单列表中添加查看操作按钮 - 实现 `get_order_detail` API 接口,提供订单详细信息 - 添加权限控制,确保只有管理员或订单所有者可访问 - 在充值历史页面也增加订单详情查看功能 - 更新表格布局以适应新增的操作列 ```
This commit is contained in:
parent
5e1f037d4c
commit
bd80414c4d
5
app.py
5
app.py
@ -156,6 +156,11 @@ def create_app():
|
|||||||
def video_page():
|
def video_page():
|
||||||
return render_template('video.html')
|
return render_template('video.html')
|
||||||
|
|
||||||
|
@app.route('/admin/orders/<int:order_id>')
|
||||||
|
def order_detail(order_id):
|
||||||
|
# 权限检查可以在这里做,或者让模板里的 JS 向 API 请求时做 (API 已有权限检查)
|
||||||
|
return render_template('order_detail.html')
|
||||||
|
|
||||||
@app.route('/files/<path:filename>')
|
@app.route('/files/<path:filename>')
|
||||||
def get_file(filename):
|
def get_file(filename):
|
||||||
"""Proxy route to serve files from MinIO via the backend"""
|
"""Proxy route to serve files from MinIO via the backend"""
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify, g
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from extensions import db
|
from extensions import db
|
||||||
from models import User, Role, Permission, SystemDict, SystemNotification, Order, PointsGrant, InviteReward, to_bj_time, get_bj_now
|
from models import User, Role, Permission, SystemDict, SystemNotification, Order, PointsGrant, InviteReward, to_bj_time, get_bj_now
|
||||||
from middlewares.auth import permission_required
|
from middlewares.auth import permission_required, login_required
|
||||||
from services.logger import system_logger
|
from services.logger import system_logger
|
||||||
|
|
||||||
admin_bp = Blueprint('admin', __name__, url_prefix='/api/admin')
|
admin_bp = Blueprint('admin', __name__, url_prefix='/api/admin')
|
||||||
@ -303,6 +303,40 @@ def get_orders():
|
|||||||
} for o in orders]
|
} for o in orders]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@admin_bp.route('/orders/<int:order_id>', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def get_order_detail(order_id):
|
||||||
|
order = db.session.get(Order, order_id)
|
||||||
|
if not order:
|
||||||
|
return jsonify({"error": "订单不存在"}), 404
|
||||||
|
|
||||||
|
# 权限检查:必须是管理员或者订单所有者
|
||||||
|
if not g.user.has_permission('manage_system') and order.user_id != g.user.id:
|
||||||
|
return jsonify({"error": "无权访问此订单"}), 403
|
||||||
|
|
||||||
|
user = order.user
|
||||||
|
return jsonify({
|
||||||
|
"order": {
|
||||||
|
"id": order.id,
|
||||||
|
"out_trade_no": order.out_trade_no,
|
||||||
|
"trade_no": order.trade_no,
|
||||||
|
"amount": float(order.amount),
|
||||||
|
"points": order.points,
|
||||||
|
"status": order.status,
|
||||||
|
"created_at": order.created_at_bj.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
"paid_at": order.paid_at_bj.strftime('%Y-%m-%d %H:%M:%S') if order.paid_at else None
|
||||||
|
},
|
||||||
|
"buyer": {
|
||||||
|
"id": user.id,
|
||||||
|
"phone": user.phone,
|
||||||
|
"current_points": user.points,
|
||||||
|
"created_at": user.created_at_bj.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
"is_banned": user.is_banned,
|
||||||
|
"role": user.role.name if user.role else "普通用户"
|
||||||
|
},
|
||||||
|
"current_is_admin": g.user.has_permission('manage_system')
|
||||||
|
})
|
||||||
|
|
||||||
# --- 积分发放管理 ---
|
# --- 积分发放管理 ---
|
||||||
@admin_bp.route('/points/grant', methods=['POST'])
|
@admin_bp.route('/points/grant', methods=['POST'])
|
||||||
@permission_required('manage_system')
|
@permission_required('manage_system')
|
||||||
|
|||||||
@ -181,7 +181,8 @@
|
|||||||
<th class="px-8 py-5">订单信息</th>
|
<th class="px-8 py-5">订单信息</th>
|
||||||
<th class="px-8 py-5">积分/金额</th>
|
<th class="px-8 py-5">积分/金额</th>
|
||||||
<th class="px-8 py-5">状态</th>
|
<th class="px-8 py-5">状态</th>
|
||||||
<th class="px-8 py-5 text-right">时间</th>
|
<th class="px-8 py-5">时间</th>
|
||||||
|
<th class="px-8 py-5">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-slate-50 text-sm font-bold text-slate-600">
|
<tbody class="divide-y divide-slate-50 text-sm font-bold text-slate-600">
|
||||||
@ -216,15 +217,21 @@
|
|||||||
<span class="text-slate-400 text-[10px]">已取消</span>
|
<span class="text-slate-400 text-[10px]">已取消</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-8 py-5 text-right text-slate-400 text-xs">
|
<td class="px-8 py-5 text-slate-400 text-xs">
|
||||||
{{ order.paid_at_bj.strftime('%Y-%m-%d %H:%M') if order.paid_at_bj else
|
{{ order.paid_at_bj.strftime('%Y-%m-%d %H:%M') if order.paid_at_bj else
|
||||||
order.created_at_bj.strftime('%Y-%m-%d %H:%M') }}
|
order.created_at_bj.strftime('%Y-%m-%d %H:%M') }}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-8 py-5">
|
||||||
|
<a href="/admin/orders/{{ order.id }}"
|
||||||
|
class="w-7 h-7 bg-slate-50 text-slate-400 hover:bg-indigo-50 hover:text-indigo-600 rounded-lg flex items-center justify-center transition-all">
|
||||||
|
<i data-lucide="eye" class="w-3.5 h-3.5"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="px-8 py-16 text-center text-slate-300 italic">
|
<td colspan="5" class="px-8 py-16 text-center text-slate-300 italic">
|
||||||
暂无充值记录
|
暂无充值记录
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
238
templates/order_detail.html
Normal file
238
templates/order_detail.html
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}订单详情 - 管理后台{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="flex-1 overflow-y-auto p-8 relative">
|
||||||
|
<div class="max-w-4xl mx-auto space-y-8">
|
||||||
|
<!-- 头部导航 -->
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<a href="/admin/orders" onclick="history.back(); return false;"
|
||||||
|
class="w-10 h-10 bg-white border border-slate-200 rounded-xl flex items-center justify-center text-slate-500 hover:text-indigo-600 transition-colors">
|
||||||
|
<i data-lucide="arrow-left" class="w-5 h-5"></i>
|
||||||
|
</a>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<h1 class="text-3xl font-black text-slate-900 tracking-tight">订单详情</h1>
|
||||||
|
<p class="text-slate-500 font-bold text-sm flex items-center gap-2">
|
||||||
|
<i data-lucide="file-text" class="w-4 h-4"></i>
|
||||||
|
订单号: <span id="outTradeNo">...</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="statusBadgeContainer">
|
||||||
|
<!-- Status badge will be injected here -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
<!-- 左侧:主要信息 -->
|
||||||
|
<div class="md:col-span-2 space-y-8">
|
||||||
|
<!-- 订单商品信息 -->
|
||||||
|
<div
|
||||||
|
class="bg-white/70 backdrop-blur-xl rounded-[2.5rem] border border-slate-200/50 shadow-2xl p-8 space-y-6">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h2 class="text-lg font-black text-slate-900 flex items-center gap-2">
|
||||||
|
<i data-lucide="package" class="w-5 h-5 text-indigo-500"></i>
|
||||||
|
商品信息
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div
|
||||||
|
class="flex items-center justify-between p-6 bg-slate-50 rounded-3xl border border-slate-100">
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<div
|
||||||
|
class="w-14 h-14 bg-amber-100 rounded-2xl flex items-center justify-center text-amber-600 shadow-sm">
|
||||||
|
<i data-lucide="zap" class="w-7 h-7"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="font-black text-slate-900">账户充值积分</h3>
|
||||||
|
<p class="text-xs text-slate-400 font-bold">虚拟商品 · 即时到账</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<div class="text-xl font-black text-slate-900">+<span id="orderPoints">0</span> Pts
|
||||||
|
</div>
|
||||||
|
<div class="text-sm font-bold text-slate-400">¥<span id="orderAmount">0.00</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-4 border-t border-slate-100 space-y-3">
|
||||||
|
<div class="flex justify-between text-sm font-bold">
|
||||||
|
<span class="text-slate-400">商品总额</span>
|
||||||
|
<span class="text-slate-600">¥<span id="orderAmount2">0.00</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between text-sm font-bold">
|
||||||
|
<span class="text-slate-400">优惠金额</span>
|
||||||
|
<span class="text-emerald-500">-¥0.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center pt-2">
|
||||||
|
<span class="text-lg font-black text-slate-900">实付款</span>
|
||||||
|
<span class="text-2xl font-black text-indigo-600">¥<span
|
||||||
|
id="orderTotal">0.00</span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付信息 -->
|
||||||
|
<div
|
||||||
|
class="bg-white/70 backdrop-blur-xl rounded-[2.5rem] border border-slate-200/50 shadow-2xl p-8 space-y-6">
|
||||||
|
<h2 class="text-lg font-black text-slate-900 flex items-center gap-2">
|
||||||
|
<i data-lucide="credit-card" class="w-5 h-5 text-indigo-500"></i>
|
||||||
|
支付信息
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-8">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest">支付渠道</label>
|
||||||
|
<div class="flex items-center gap-2 font-bold text-slate-700">
|
||||||
|
支付宝 (Alipay)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<label
|
||||||
|
class="text-[10px] font-black text-slate-400 uppercase tracking-widest">支付宝流水号</label>
|
||||||
|
<p id="tradeNo" class="font-mono text-xs font-bold text-slate-700">--</p>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest">创建时间</label>
|
||||||
|
<p id="createdAt" class="text-sm font-bold text-slate-700">--</p>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<label class="text-[10px] font-black text-slate-400 uppercase tracking-widest">支付时间</label>
|
||||||
|
<p id="paidAt" class="text-sm font-bold text-slate-700">--</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧:买家信息 -->
|
||||||
|
<div class="space-y-8">
|
||||||
|
<div
|
||||||
|
class="bg-white/70 backdrop-blur-xl rounded-[2.5rem] border border-slate-200/50 shadow-2xl p-8 space-y-6">
|
||||||
|
<h2 class="text-lg font-black text-slate-900 flex items-center gap-2">
|
||||||
|
<i data-lucide="user" class="w-5 h-5 text-indigo-500"></i>
|
||||||
|
买家信息
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="flex flex-col items-center text-center space-y-4 pb-4">
|
||||||
|
<div
|
||||||
|
class="w-20 h-20 bg-indigo-50 rounded-[2rem] flex items-center justify-center text-indigo-600 shadow-inner">
|
||||||
|
<i data-lucide="user" class="w-10 h-10"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 id="userPhone" class="text-xl font-black text-slate-900">--</h3>
|
||||||
|
<div class="flex items-center justify-center gap-2 mt-1">
|
||||||
|
<span id="userRole"
|
||||||
|
class="px-2 py-0.5 bg-slate-100 text-slate-500 text-[10px] font-black rounded-md border border-slate-200 uppercase">--</span>
|
||||||
|
<span id="userBanned"
|
||||||
|
class="hidden px-2 py-0.5 bg-rose-50 text-rose-500 text-[10px] font-black rounded-md border border-rose-100 uppercase">已封禁</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-6 border-t border-slate-100 space-y-4">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-xs font-bold text-slate-400">用户 ID</span>
|
||||||
|
<span id="userId" class="text-sm font-black text-slate-700">#0</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-xs font-bold text-slate-400">注册时间</span>
|
||||||
|
<span id="userCreatedAt" class="text-sm font-black text-slate-700 text-right">--</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="text-xs font-bold text-slate-400">当前余额</span>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<i data-lucide="zap" class="w-3 h-3 text-amber-500"></i>
|
||||||
|
<span id="userCurrentPoints" class="text-sm font-black text-slate-900">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a id="viewUserBtn" href="#"
|
||||||
|
class="hidden w-full py-3 bg-slate-50 hover:bg-slate-100 text-slate-500 hover:text-indigo-600 transition-all rounded-2xl text-center text-xs font-black border border-slate-100 mt-4">
|
||||||
|
查看用户详细档案
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 订单备注 (示例) -->
|
||||||
|
<div class="bg-indigo-600 rounded-[2.5rem] p-8 text-white space-y-4 shadow-2xl shadow-indigo-200">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<i data-lucide="info" class="w-5 h-5 text-indigo-200"></i>
|
||||||
|
<h3 class="font-black">系统提示</h3>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-indigo-100 font-medium leading-relaxed">
|
||||||
|
该订单由系统自动处理并完成积分发放。如有异常,请及时联系技术人员查看服务器日志。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script>
|
||||||
|
const orderId = window.location.pathname.split('/').pop();
|
||||||
|
|
||||||
|
async function loadOrderDetail() {
|
||||||
|
try {
|
||||||
|
const r = await fetch(`/api/admin/orders/${orderId}`);
|
||||||
|
if (!r.ok) throw new Error('订单未找到');
|
||||||
|
const data = await r.json();
|
||||||
|
renderDetail(data);
|
||||||
|
} catch (e) {
|
||||||
|
showToast('加载详情失败: ' + e.message, 'error');
|
||||||
|
setTimeout(() => history.back(), 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderDetail(data) {
|
||||||
|
const { order, buyer, current_is_admin } = data;
|
||||||
|
|
||||||
|
// 订单基本信息
|
||||||
|
document.getElementById('outTradeNo').textContent = order.out_trade_no;
|
||||||
|
document.getElementById('orderPoints').textContent = order.points;
|
||||||
|
document.getElementById('orderAmount').textContent = order.amount.toFixed(2);
|
||||||
|
document.getElementById('orderAmount2').textContent = order.amount.toFixed(2);
|
||||||
|
document.getElementById('orderTotal').textContent = order.amount.toFixed(2);
|
||||||
|
document.getElementById('tradeNo').textContent = order.trade_no || '--';
|
||||||
|
document.getElementById('createdAt').textContent = order.created_at;
|
||||||
|
document.getElementById('paidAt').textContent = order.paid_at || '--';
|
||||||
|
|
||||||
|
// 状态徽章
|
||||||
|
const badgeContainer = document.getElementById('statusBadgeContainer');
|
||||||
|
if (order.status === 'PAID') {
|
||||||
|
badgeContainer.innerHTML = '<span class="px-5 py-2 bg-emerald-50 text-emerald-600 text-sm font-black rounded-xl border border-emerald-100 shadow-sm shadow-emerald-50 flex items-center gap-2"><i data-lucide="check-circle" class="w-4 h-4"></i>已完成</span>';
|
||||||
|
} else if (order.status === 'PENDING') {
|
||||||
|
badgeContainer.innerHTML = '<span class="px-5 py-2 bg-amber-50 text-amber-600 text-sm font-black rounded-xl border border-amber-100 shadow-sm shadow-amber-50 flex items-center gap-2"><i data-lucide="clock" class="w-4 h-4"></i>待支付</span>';
|
||||||
|
} else {
|
||||||
|
badgeContainer.innerHTML = '<span class="px-5 py-2 bg-slate-50 text-slate-400 text-sm font-black rounded-xl border border-slate-100 shadow-sm shadow-slate-50 flex items-center gap-2"><i data-lucide="x-circle" class="w-4 h-4"></i>已取消</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 买家信息
|
||||||
|
document.getElementById('userPhone').textContent = buyer.phone;
|
||||||
|
document.getElementById('userRole').textContent = buyer.role;
|
||||||
|
document.getElementById('userId').textContent = '#' + buyer.id;
|
||||||
|
document.getElementById('userCreatedAt').textContent = buyer.created_at;
|
||||||
|
document.getElementById('userCurrentPoints').textContent = buyer.current_points;
|
||||||
|
|
||||||
|
if (buyer.is_banned) {
|
||||||
|
document.getElementById('userBanned').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 权限相关
|
||||||
|
const viewBtn = document.getElementById('viewUserBtn');
|
||||||
|
if (current_is_admin) {
|
||||||
|
viewBtn.classList.remove('hidden');
|
||||||
|
viewBtn.href = `/api/admin/users?q=${buyer.phone}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadOrderDetail();
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@ -16,31 +16,41 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input type="text" id="orderSearch" placeholder="搜索手机号/订单号..." class="pl-10 pr-4 py-2 bg-white border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-indigo-500 outline-none transition-all w-64">
|
<input type="text" id="orderSearch" placeholder="搜索手机号/订单号..."
|
||||||
|
class="pl-10 pr-4 py-2 bg-white border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-indigo-500 outline-none transition-all w-64">
|
||||||
<i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>
|
<i data-lucide="search" class="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400"></i>
|
||||||
</div>
|
</div>
|
||||||
<button onclick="loadOrders()" class="w-10 h-10 bg-white border border-slate-200 rounded-xl flex items-center justify-center text-slate-500 hover:text-indigo-600 transition-colors">
|
<button onclick="loadOrders()"
|
||||||
|
class="w-10 h-10 bg-white border border-slate-200 rounded-xl flex items-center justify-center text-slate-500 hover:text-indigo-600 transition-colors">
|
||||||
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
|
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 记录列表 -->
|
<!-- 记录列表 -->
|
||||||
<div class="bg-white/70 backdrop-blur-xl rounded-[2.5rem] border border-slate-200/50 shadow-2xl overflow-hidden">
|
<div
|
||||||
|
class="bg-white/70 backdrop-blur-xl rounded-[2.5rem] border border-slate-200/50 shadow-2xl overflow-hidden">
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<table class="w-full text-left border-collapse">
|
<table class="w-full text-left border-collapse">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="border-b border-slate-100 bg-slate-50/50">
|
<tr class="border-b border-slate-100 bg-slate-50/50">
|
||||||
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">用户信息</th>
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">用户信息
|
||||||
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">订单详情</th>
|
</th>
|
||||||
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">积分/金额</th>
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">订单详情
|
||||||
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">状态</th>
|
</th>
|
||||||
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">时间</th>
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">积分/金额
|
||||||
|
</th>
|
||||||
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">状态
|
||||||
|
</th>
|
||||||
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">时间
|
||||||
|
</th>
|
||||||
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">操作
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="orderList" class="divide-y divide-slate-100">
|
<tbody id="orderList" class="divide-y divide-slate-100">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="px-8 py-20 text-center">
|
<td colspan="6" class="px-8 py-20 text-center">
|
||||||
<div class="flex flex-col items-center gap-4 animate-pulse">
|
<div class="flex flex-col items-center gap-4 animate-pulse">
|
||||||
<i data-lucide="loader-2" class="w-8 h-8 text-indigo-500 animate-spin"></i>
|
<i data-lucide="loader-2" class="w-8 h-8 text-indigo-500 animate-spin"></i>
|
||||||
<p class="text-slate-400 font-bold">正在获取记录...</p>
|
<p class="text-slate-400 font-bold">正在获取记录...</p>
|
||||||
@ -73,17 +83,7 @@
|
|||||||
function renderOrders(orders) {
|
function renderOrders(orders) {
|
||||||
const list = document.getElementById('orderList');
|
const list = document.getElementById('orderList');
|
||||||
if (orders.length === 0) {
|
if (orders.length === 0) {
|
||||||
list.innerHTML = `
|
renderEmptyState();
|
||||||
<tr>
|
|
||||||
<td colspan="5" class="px-8 py-20 text-center">
|
|
||||||
<div class="flex flex-col items-center gap-4 opacity-20">
|
|
||||||
<i data-lucide="inbox" class="w-16 h-16"></i>
|
|
||||||
<p class="font-black text-xl">暂无记录</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
lucide.createIcons();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,23 +117,43 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="px-8 py-5">
|
<td class="px-8 py-5">
|
||||||
${o.status === 'PAID'
|
${o.status === 'PAID'
|
||||||
? '<span class="px-3 py-1 bg-emerald-50 text-emerald-600 text-[10px] font-black rounded-lg border border-emerald-100">已完成</span>'
|
? '<span class="px-3 py-1 bg-emerald-50 text-emerald-600 text-[10px] font-black rounded-lg border border-emerald-100">已完成</span>'
|
||||||
: o.status === 'PENDING'
|
: o.status === 'PENDING'
|
||||||
? '<span class="px-3 py-1 bg-amber-50 text-amber-600 text-[10px] font-black rounded-lg border border-amber-100">待支付</span>'
|
? '<span class="px-3 py-1 bg-amber-50 text-amber-600 text-[10px] font-black rounded-lg border border-amber-100">待支付</span>'
|
||||||
: '<span class="px-3 py-1 bg-slate-50 text-slate-400 text-[10px] font-black rounded-lg border border-slate-100">已取消</span>'
|
: '<span class="px-3 py-1 bg-slate-50 text-slate-400 text-[10px] font-black rounded-lg border border-slate-100">已取消</span>'
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-8 py-5">
|
<td class="px-8 py-5 text-right">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="text-[10px] font-bold text-slate-500">创建: ${o.created_at}</span>
|
<span class="text-[10px] font-bold text-slate-500">创建: ${o.created_at}</span>
|
||||||
<span class="text-[10px] font-bold text-emerald-500">${o.paid_at ? '完成: ' + o.paid_at : ''}</span>
|
<span class="text-[10px] font-bold text-emerald-500">${o.paid_at ? '完成: ' + o.paid_at : ''}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-8 py-5">
|
||||||
|
<a href="/admin/orders/${o.id}" class="w-8 h-8 bg-slate-50 text-slate-400 hover:bg-indigo-50 hover:text-indigo-600 rounded-lg flex items-center justify-center transition-all">
|
||||||
|
<i data-lucide="eye" class="w-4 h-4"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`).join('');
|
`).join('');
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderEmptyState() {
|
||||||
|
const list = document.getElementById('orderList');
|
||||||
|
list.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="px-8 py-20 text-center">
|
||||||
|
<div class="flex flex-col items-center gap-4 opacity-20">
|
||||||
|
<i data-lucide="inbox" class="w-16 h-16"></i>
|
||||||
|
<p class="font-black text-xl">暂无记录</p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('orderSearch').oninput = (e) => {
|
document.getElementById('orderSearch').oninput = (e) => {
|
||||||
const val = e.target.value.toLowerCase();
|
const val = e.target.value.toLowerCase();
|
||||||
const filtered = allOrders.filter(o =>
|
const filtered = allOrders.filter(o =>
|
||||||
|
|||||||
@ -38,6 +38,8 @@
|
|||||||
</th>
|
</th>
|
||||||
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">支付时间
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">支付时间
|
||||||
</th>
|
</th>
|
||||||
|
<th class="px-8 py-5 text-[10px] font-black text-slate-400 uppercase tracking-widest">操作
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-slate-100">
|
<tbody class="divide-y divide-slate-100">
|
||||||
@ -84,11 +86,17 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="px-8 py-5">
|
||||||
|
<a href="/admin/orders/{{ order.id }}"
|
||||||
|
class="w-8 h-8 bg-slate-50 text-slate-400 hover:bg-indigo-50 hover:text-indigo-600 rounded-lg flex items-center justify-center transition-all">
|
||||||
|
<i data-lucide="eye" class="w-4 h-4"></i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="px-8 py-20 text-center">
|
<td colspan="6" class="px-8 py-20 text-center">
|
||||||
<div class="flex flex-col items-center gap-4 opacity-20">
|
<div class="flex flex-col items-center gap-4 opacity-20">
|
||||||
<i data-lucide="inbox" class="w-16 h-16"></i>
|
<i data-lucide="inbox" class="w-16 h-16"></i>
|
||||||
<p class="font-black text-xl">暂无充值记录</p>
|
<p class="font-black text-xl">暂无充值记录</p>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user