feat(admin): 添加订单详情页面和API接口 - 新增 `/admin/orders/<int:order_id>` 路由用于显示订单详情页面 - 在管理后台的订单列表中添加查看操作按钮 - 实现 `get_order_detail` API 接口,提供订单详细信息 - 添加权限控制,确保只有管理员或订单所有者可访问 - 在充值历史页面也增加订单详情查看功能 - 更新表格布局以适应新增的操作列 ```
291 lines
16 KiB
HTML
291 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}充值与邀请 - AI 视界{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="w-full h-full overflow-y-auto p-4 lg:p-10 custom-scrollbar bg-slate-50/20">
|
|
<div class="max-w-6xl w-full mx-auto flex flex-col space-y-12 pb-20">
|
|
|
|
<!-- Top Header -->
|
|
<div class="flex items-center justify-between shrink-0">
|
|
<div class="flex items-center gap-5">
|
|
<div
|
|
class="w-14 h-14 bg-gradient-to-br from-indigo-500 to-indigo-700 text-white rounded-2xl flex items-center justify-center shadow-2xl shadow-indigo-100">
|
|
<i data-lucide="crown" class="w-8 h-8"></i>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-3xl font-black text-slate-900 tracking-tight">充值中心</h1>
|
|
<p class="text-slate-400 text-xs font-black uppercase tracking-widest opacity-60">Recharge & Invite
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
{% if is_admin %}
|
|
<a href="/admin/orders"
|
|
class="p-3 bg-white/50 text-indigo-600 border border-indigo-100 rounded-2xl hover:bg-white transition-colors flex items-center gap-2 font-bold text-xs">
|
|
<i data-lucide="shield-check" class="w-4 h-4"></i>
|
|
管理后台
|
|
</a>
|
|
{% endif %}
|
|
<button onclick="window.history.back()"
|
|
class="p-3 text-slate-300 hover:text-slate-600 transition-colors">
|
|
<i data-lucide="x" class="w-7 h-7"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 1. Recharge Packages Section -->
|
|
<div class="space-y-6">
|
|
<div class="flex items-center justify-between px-2">
|
|
<h3 class="text-sm font-black text-slate-900 tracking-widest uppercase italic">选择充值套餐</h3>
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-2 h-2 bg-emerald-500 rounded-full animate-pulse"></div>
|
|
<span class="text-[9px] font-black text-slate-400">即时到账</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<!-- Package 50 -->
|
|
<div onclick="submitRecharge('50')"
|
|
class="bg-white p-6 rounded-[2.5rem] border border-slate-100 shadow-xl shadow-slate-200/30 hover:border-indigo-400 cursor-pointer transition-all group relative overflow-hidden">
|
|
<div class="flex justify-between items-start mb-4 relative z-10">
|
|
<div
|
|
class="w-10 h-10 bg-slate-50 rounded-xl flex items-center justify-center text-slate-400 group-hover:text-indigo-600 transition-colors">
|
|
<i data-lucide="zap" class="w-5 h-5"></i>
|
|
</div>
|
|
<span class="text-lg font-black text-slate-900">¥5.00</span>
|
|
</div>
|
|
<div class="relative z-10">
|
|
<h5 class="text-xl font-black text-slate-800">50 <small
|
|
class="text-[10px] opacity-40">Pts</small></h5>
|
|
<p class="text-[9px] font-bold text-slate-400 mt-1">个人尝鲜包</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Package 200 (Hot) -->
|
|
<div onclick="submitRecharge('200')"
|
|
class="bg-white p-6 rounded-[2.5rem] border-2 border-indigo-500 shadow-xl shadow-indigo-100/40 hover:scale-105 cursor-pointer transition-all relative ring-4 ring-indigo-50/50">
|
|
<div
|
|
class="absolute -top-3 -right-3 px-3 py-1 bg-indigo-600 text-white rounded-full text-[9px] font-black shadow-lg z-20">
|
|
HOT</div>
|
|
<div class="flex justify-between items-start mb-4">
|
|
<div class="w-10 h-10 bg-indigo-50 rounded-xl flex items-center justify-center text-indigo-600">
|
|
<i data-lucide="zap" class="w-5 h-5"></i>
|
|
</div>
|
|
<span class="text-lg font-black text-slate-900">¥20.00</span>
|
|
</div>
|
|
<h5 class="text-xl font-black text-slate-800">200 <small class="text-[10px] opacity-40">Pts</small>
|
|
</h5>
|
|
<p class="text-[9px] font-bold text-indigo-500 mt-1 uppercase">全站最受欢迎</p>
|
|
</div>
|
|
|
|
<!-- Package 1000 -->
|
|
<div onclick="submitRecharge('1000')"
|
|
class="bg-white p-6 rounded-[2.5rem] border border-slate-100 shadow-xl shadow-slate-200/30 hover:border-indigo-400 cursor-pointer transition-all group">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<div
|
|
class="w-10 h-10 bg-slate-50 rounded-xl flex items-center justify-center text-slate-400 group-hover:text-indigo-600 transition-colors">
|
|
<i data-lucide="zap" class="w-5 h-5"></i>
|
|
</div>
|
|
<span class="text-lg font-black text-slate-900">¥100.00</span>
|
|
</div>
|
|
<h5 class="text-xl font-black text-slate-800">1000 <small class="text-[10px] opacity-40">Pts</small>
|
|
</h5>
|
|
<p class="text-[9px] font-bold text-slate-400 mt-1">工作室/创作包</p>
|
|
</div>
|
|
|
|
<!-- Package 5000 -->
|
|
<div onclick="submitRecharge('5000')"
|
|
class="bg-slate-900 p-6 rounded-[2.5rem] border border-slate-800 shadow-2xl hover:bg-slate-800 cursor-pointer transition-all group text-white">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<div class="w-10 h-10 bg-white/10 rounded-xl flex items-center justify-center text-indigo-400">
|
|
<i data-lucide="crown" class="w-5 h-5"></i>
|
|
</div>
|
|
<span class="text-lg font-black">¥500.00</span>
|
|
</div>
|
|
<h5 class="text-xl font-black">5000 <small class="text-[10px] opacity-40">Pts</small></h5>
|
|
<p class="text-[9px] font-bold text-indigo-300 mt-1">旗舰版海量包</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form id="rechargeForm" action="/payment/create" method="POST" class="hidden">
|
|
<input type="hidden" name="package_id" id="packageIdInput">
|
|
</form>
|
|
</div>
|
|
|
|
<!-- 2. Invitation Section -->
|
|
<div id="invite-section"
|
|
class="bg-white rounded-[3rem] shadow-xl shadow-slate-200/50 border border-slate-100 overflow-hidden relative">
|
|
<div class="absolute top-0 right-0 p-6 opacity-5">
|
|
<i data-lucide="gift" class="w-64 h-64"></i>
|
|
</div>
|
|
|
|
<div class="p-10 relative z-10 flex flex-col md:flex-row items-center gap-12">
|
|
<div class="flex-1 space-y-6">
|
|
<div>
|
|
<h4 class="text-xl font-black text-slate-900 tracking-tight flex items-center gap-3">
|
|
<span class="text-indigo-600">邀请好友</span> 赚取佣金
|
|
</h4>
|
|
<p class="text-sm font-bold text-slate-400 mt-2 leading-relaxed">
|
|
每成功邀请一位好友注册,您将获得其前 3 次充值金额的 <span class="text-indigo-600 text-base">10%</span>
|
|
等值积分奖励。实时到账,无上限。
|
|
</p>
|
|
</div>
|
|
|
|
<div
|
|
class="bg-slate-50 border border-slate-100 p-6 rounded-[2rem] flex flex-col sm:flex-row items-center gap-6">
|
|
<div class="flex-1 w-full space-y-1">
|
|
<p class="text-[9px] font-black text-slate-400 uppercase tracking-widest">我的专属邀请码</p>
|
|
<div class="flex items-center gap-4">
|
|
<span id="myInviteCode"
|
|
class="text-3xl font-black text-indigo-900 tracking-widest cursor-pointer"
|
|
onclick="copyInviteLink()" title="点击复制链接">...</span>
|
|
<button onclick="copyInviteLink()"
|
|
class="p-2 bg-white border border-slate-200 rounded-xl hover:text-indigo-600 hover:border-indigo-200 transition-all">
|
|
<i data-lucide="link" class="w-4 h-4"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="h-10 w-px bg-slate-200 hidden sm:block"></div>
|
|
<div class="flex gap-8">
|
|
<div class="text-center">
|
|
<p class="text-[9px] font-black text-slate-400">已邀请</p>
|
|
<p id="myInvitedCount" class="text-xl font-black text-slate-800">0</p>
|
|
</div>
|
|
<div class="text-center">
|
|
<p class="text-[9px] font-black text-slate-400">累计获取</p>
|
|
<p id="myTotalRewards" class="text-xl font-black text-amber-500">0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Illustration or Right Side -->
|
|
<div class="hidden md:flex items-center justify-center">
|
|
<div
|
|
class="w-20 h-20 bg-indigo-50 rounded-full flex items-center justify-center text-indigo-500 animate-bounce">
|
|
<i data-lucide="gift" class="w-8 h-8"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3. Recharge History (Order List) -->
|
|
<div class="space-y-6 pt-6">
|
|
<h3 class="text-sm font-black text-slate-900 tracking-widest uppercase italic px-2">最近充值记录</h3>
|
|
|
|
<div class="bg-white rounded-[2.5rem] shadow-sm border border-slate-100 overflow-hidden">
|
|
<table class="w-full text-left">
|
|
<thead>
|
|
<tr class="bg-slate-50/50 text-[10px] font-black text-slate-400 uppercase tracking-widest">
|
|
<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>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-50 text-sm font-bold text-slate-600">
|
|
{% if personal_orders %}
|
|
{% for order in personal_orders %}
|
|
<tr class="hover:bg-slate-50/30 transition-colors">
|
|
<td class="px-8 py-5">
|
|
<div class="flex flex-col">
|
|
<span class="text-xs text-slate-900">{{ order.out_trade_no }}</span>
|
|
<span class="text-[10px] text-slate-400 font-mono">Ali: {{ order.trade_no or '-'
|
|
}}</span>
|
|
</div>
|
|
</td>
|
|
<td class="px-8 py-5">
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-indigo-600">+{{ order.points }} Pts</span>
|
|
<span class="text-[10px] text-slate-400">(¥{{ order.amount }})</span>
|
|
</div>
|
|
</td>
|
|
<td class="px-8 py-5">
|
|
{% if order.status == 'PAID' %}
|
|
<span
|
|
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-emerald-50 text-emerald-600 text-[10px] font-black">
|
|
<span class="w-1.5 h-1.5 bg-emerald-500 rounded-full"></span> 已完成
|
|
</span>
|
|
{% elif order.status == 'PENDING' %}
|
|
<span
|
|
class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-amber-50 text-amber-600 text-[10px] font-black">
|
|
<span class="w-1.5 h-1.5 bg-amber-500 rounded-full animate-pulse"></span> 待支付
|
|
</span>
|
|
{% else %}
|
|
<span class="text-slate-400 text-[10px]">已取消</span>
|
|
{% endif %}
|
|
</td>
|
|
<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.created_at_bj.strftime('%Y-%m-%d %H:%M') }}
|
|
</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>
|
|
{% endfor %}
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="5" class="px-8 py-16 text-center text-slate-300 italic">
|
|
暂无充值记录
|
|
</td>
|
|
</tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% if personal_orders|length >= 10 %}
|
|
<div class="text-center pt-2">
|
|
<a href="/recharge_history" class="text-xs font-bold text-indigo-500 hover:text-indigo-600">查看更多记录
|
|
-></a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
// --- Recharge Logic ---
|
|
function submitRecharge(pkgId) {
|
|
document.getElementById('packageIdInput').value = pkgId;
|
|
document.getElementById('rechargeForm').submit();
|
|
}
|
|
|
|
// --- Invite Logic ---
|
|
async function loadInviteStats() {
|
|
try {
|
|
const r = await fetch('/api/auth/invite_stats');
|
|
const d = await r.json();
|
|
if (d.invite_code) {
|
|
document.getElementById('myInviteCode').innerText = d.invite_code;
|
|
document.getElementById('myInvitedCount').innerText = d.invited_count;
|
|
document.getElementById('myTotalRewards').innerText = d.total_rewards;
|
|
}
|
|
} catch (e) { console.error('加载邀请统计失败', e); }
|
|
}
|
|
|
|
function copyInviteLink() {
|
|
const code = document.getElementById('myInviteCode').innerText;
|
|
if (code === '...') return;
|
|
|
|
const link = `${window.location.origin}/login?invite_code=${code}`;
|
|
navigator.clipboard.writeText(link).then(() => {
|
|
showToast('专属邀请链接已复制', 'success');
|
|
});
|
|
}
|
|
|
|
// Init
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
loadInviteStats();
|
|
});
|
|
</script>
|
|
{% endblock %} |