Files
pk10/App/Views/Admin/fund_requests.php
T
li 4a94fe36cf feat: 重构代码架构 + 新增报表/跟单/输赢统计功能
- 拆分 HomeController → TransactionController, ReportWebController, FollowPlanController
- 新增 Service 层: TransactionService, ReportService, FollowPlanService
- pk10.php JS 抽离为 4 个独立文件 (sound/race/bet/poll)
- 前台新增报表查询页面 (/report) + 跟单计划页面 (/follow-plan)
- 后台新增跟单计划管理 + 用户输赢明细统计
- 封盘状态显示倒计时 (x:xx)
- 音效仅在开奖弹窗打开时播放
- 路由按模块分组整理
- autoload 支持 App\Services 命名空间
2026-03-27 18:41:06 +08:00

307 lines
16 KiB
PHP

<h1 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
<i class="fas fa-file-invoice-dollar text-primary mr-3"></i>
充提审核
</h1>
<!-- 统计卡片 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="bg-white rounded-xl p-5 card-shadow hover-lift">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm">待处理</p>
<h3 class="text-xl font-bold mt-1 text-warning" id="statPending">
<?= (int)($pendingCount ?? 0) ?>
</h3>
</div>
<div class="w-10 h-10 rounded-full bg-warning/10 flex items-center justify-center">
<i class="fas fa-clock text-warning"></i>
</div>
</div>
</div>
<div class="bg-white rounded-xl p-5 card-shadow hover-lift">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm">今日充值申请</p>
<h3 class="text-xl font-bold mt-1 text-success">
<?= (int)($todayDeposit ?? 0) ?>
</h3>
</div>
<div class="w-10 h-10 rounded-full bg-success/10 flex items-center justify-center">
<i class="fas fa-arrow-down text-success"></i>
</div>
</div>
</div>
<div class="bg-white rounded-xl p-5 card-shadow hover-lift">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm">今日提现申请</p>
<h3 class="text-xl font-bold mt-1 text-danger">
<?= (int)($todayWithdraw ?? 0) ?>
</h3>
</div>
<div class="w-10 h-10 rounded-full bg-danger/10 flex items-center justify-center">
<i class="fas fa-arrow-up text-danger"></i>
</div>
</div>
</div>
</div>
<!-- 筛选栏 -->
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
<div class="flex flex-col md:flex-row md:items-center gap-4">
<div class="flex items-center gap-2">
<label class="text-sm text-gray-600 whitespace-nowrap">类型:</label>
<select id="filterType" class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary/50 focus:border-primary">
<option value="">全部</option>
<option value="deposit">充值</option>
<option value="withdraw">提现</option>
</select>
</div>
<div class="flex items-center gap-2">
<label class="text-sm text-gray-600 whitespace-nowrap">状态:</label>
<select id="filterStatus" class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary/50 focus:border-primary">
<option value="">全部</option>
<option value="pending">待处理</option>
<option value="approved">已通过</option>
<option value="rejected">已拒绝</option>
</select>
</div>
<button id="btnFilter" class="px-4 py-2 bg-primary text-white rounded-lg text-sm hover:bg-primary/90 transition-colors">
<i class="fas fa-search mr-1"></i>筛选
</button>
</div>
</div>
<!-- 申请列表 -->
<div class="bg-white rounded-xl shadow-md p-6 mb-8">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
<div>
<h2 class="text-xl font-semibold text-gray-800">充提申请列表</h2>
<p class="text-sm text-gray-500 mt-1">审核用户的充值和提现申请</p>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full bg-white rounded-xl overflow-hidden">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">用户</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">钱包地址</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">类型</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">金额</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">状态</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell">用户备注</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell">管理员备注</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden lg:table-cell">创建时间</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200" id="requestList">
<?php if (!empty($requests) && is_array($requests)): ?>
<?php foreach ($requests as $req): ?>
<tr class="hover:bg-gray-50 transition-colors fund-row"
data-type="<?= htmlspecialchars((string)($req['type'] ?? '')) ?>"
data-status="<?= htmlspecialchars((string)($req['status'] ?? '')) ?>">
<td class="px-4 py-4">
<span class="text-sm font-medium text-gray-900">#<?= htmlspecialchars((string)($req['id'] ?? '')) ?></span>
</td>
<td class="px-4 py-4">
<div class="text-sm text-gray-900"><?= htmlspecialchars((string)($req['username'] ?? '未知')) ?></div>
<div class="text-xs text-gray-500">ID: <?= htmlspecialchars((string)($req['user_id'] ?? '')) ?></div>
</td>
<td class="px-4 py-4">
<?php $addr = $req['usdt_address'] ?? ''; ?>
<?php if ($addr): ?>
<div class="text-xs text-gray-700 font-mono max-w-[160px] truncate cursor-pointer" title="<?= htmlspecialchars($addr) ?>" onclick="navigator.clipboard.writeText('<?= htmlspecialchars($addr) ?>');layui.layer&&layui.layer.msg('已复制')">
<?= htmlspecialchars($addr) ?>
</div>
<?php else: ?>
<span class="text-xs text-gray-400">未绑定</span>
<?php endif; ?>
</td>
<td class="px-4 py-4">
<?php if (($req['type'] ?? '') === 'deposit'): ?>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-success/10 text-success">充值</span>
<?php else: ?>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-danger/10 text-danger">提现</span>
<?php endif; ?>
</td>
<td class="px-4 py-4">
<span class="text-sm font-semibold text-gray-900">
<?= number_format(floatval($req['amount'] ?? 0), 2) ?>
</span>
</td>
<td class="px-4 py-4">
<?php
$status = $req['status'] ?? '';
$statusMap = [
'pending' => ['text' => '待处理', 'class' => 'bg-warning/10 text-warning'],
'approved' => ['text' => '已通过', 'class' => 'bg-success/10 text-success'],
'rejected' => ['text' => '已拒绝', 'class' => 'bg-danger/10 text-danger'],
];
$statusInfo = $statusMap[$status] ?? ['text' => $status, 'class' => 'bg-gray-100 text-gray-700'];
?>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs <?= $statusInfo['class'] ?>">
<?= $statusInfo['text'] ?>
</span>
</td>
<td class="px-4 py-4 hidden md:table-cell">
<span class="text-sm text-gray-600 max-w-[120px] truncate block">
<?= htmlspecialchars((string)($req['remark'] ?? '-')) ?>
</span>
</td>
<td class="px-4 py-4 hidden md:table-cell">
<span class="text-sm text-gray-600 max-w-[120px] truncate block">
<?= htmlspecialchars((string)($req['admin_remark'] ?? '-')) ?>
</span>
</td>
<td class="px-4 py-4 hidden lg:table-cell">
<?php if (!empty($req['created_at'])): ?>
<div class="text-xs text-gray-500">
<?= date('Y-m-d H:i:s', strtotime((string)$req['created_at'])) ?>
</div>
<?php else: ?>
<span class="text-xs text-gray-400">-</span>
<?php endif; ?>
</td>
<td class="px-4 py-4">
<?php if ($status === 'pending'): ?>
<div class="flex gap-2">
<button onclick="approveRequest(<?= (int)$req['id'] ?>)"
class="px-3 py-1 bg-success text-white text-xs rounded-lg hover:bg-success/90 transition-colors">
<i class="fas fa-check mr-1"></i>通过
</button>
<button onclick="rejectRequest(<?= (int)$req['id'] ?>)"
class="px-3 py-1 bg-danger text-white text-xs rounded-lg hover:bg-danger/90 transition-colors">
<i class="fas fa-times mr-1"></i>拒绝
</button>
</div>
<?php else: ?>
<span class="text-xs text-gray-400">已处理</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="10" class="px-6 py-12 text-center">
<div class="flex flex-col items-center">
<i class="fas fa-file-invoice text-gray-300 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900">暂无充提申请</h3>
<p class="mt-1 text-gray-500 text-sm">还没有任何充提申请记录。</p>
</div>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<script src="/Static/js/admin.js"></script>
<script>
layui.use(['layer'], function() {
var layer = layui.layer;
// --- 筛选逻辑 ---
document.getElementById('btnFilter').addEventListener('click', function() {
var typeVal = document.getElementById('filterType').value;
var statusVal = document.getElementById('filterStatus').value;
var rows = document.querySelectorAll('.fund-row');
rows.forEach(function(row) {
var show = true;
if (typeVal && row.dataset.type !== typeVal) show = false;
if (statusVal && row.dataset.status !== statusVal) show = false;
row.style.display = show ? '' : 'none';
});
});
// --- 审批通过 ---
window.approveRequest = function(id) {
layer.prompt({title: '审批通过 - 管理员备注(可留空)', formType: 2, value: ''}, function(remark, promptIndex) {
layer.close(promptIndex);
var loadIdx = layer.load(1);
fetch('/admin/fund-requests/approve', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({id: id, admin_remark: remark})
})
.then(function(r){ return r.json(); })
.then(function(d){
layer.close(loadIdx);
if(d.success){
layer.msg('审批通过', {icon: 1});
setTimeout(function(){ location.reload(); }, 800);
} else {
layer.msg(d.message || '操作失败', {icon: 2});
}
})
.catch(function(){
layer.close(loadIdx);
layer.msg('网络错误', {icon: 2});
});
});
};
// --- 拒绝 ---
window.rejectRequest = function(id) {
layer.prompt({title: '拒绝原因(可留空)', formType: 2, value: ''}, function(remark, promptIndex) {
layer.close(promptIndex);
var loadIdx = layer.load(1);
fetch('/admin/fund-requests/reject', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({id: id, admin_remark: remark})
})
.then(function(r){ return r.json(); })
.then(function(d){
layer.close(loadIdx);
if(d.success){
layer.msg('已拒绝', {icon: 1});
setTimeout(function(){ location.reload(); }, 800);
} else {
layer.msg(d.message || '操作失败', {icon: 2});
}
})
.catch(function(){
layer.close(loadIdx);
layer.msg('网络错误', {icon: 2});
});
});
};
// --- 语音提醒 + 标题闪烁轮询 ---
var lastPendingCount = <?= (int)($pendingCount ?? 0) ?>;
var originalTitle = document.title;
setInterval(function() {
fetch('/admin/fund-requests/pending-count')
.then(function(r){ return r.json(); })
.then(function(d){
var count = d.count || 0;
document.getElementById('statPending').textContent = count;
if (count > lastPendingCount) {
// 播放短促提示音
try {
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var osc = audioCtx.createOscillator();
var gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.frequency.value = 880;
osc.type = 'sine';
gain.gain.value = 0.3;
osc.start();
osc.stop(audioCtx.currentTime + 0.15);
} catch(e){}
// 标题闪烁
document.title = '⚠️ 新充提申请(' + count + ')';
setTimeout(function(){ document.title = originalTitle; }, 3000);
}
lastPendingCount = count;
})
.catch(function(){});
}, 15000);
});
</script>