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 命名空间
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
<h1 class="text-2xl font-bold text-gray-800 mb-6 flex items-center">
|
||||
<i class="fas fa-clipboard-list text-primary mr-3"></i>
|
||||
跟单计划管理
|
||||
</h1>
|
||||
|
||||
<!-- 新建按钮 -->
|
||||
<div class="mb-4">
|
||||
<button onclick="showAddForm()" class="px-5 py-2 bg-primary text-white rounded-lg text-sm hover:opacity-90 transition">
|
||||
<i class="fas fa-plus mr-1"></i> 新建计划
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 计划列表 -->
|
||||
<div class="bg-white rounded-xl shadow-md p-6 mb-6">
|
||||
<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">ID</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">计划名称</th>
|
||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">类型</th>
|
||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">名次</th>
|
||||
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase">建议金额</th>
|
||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">总期数</th>
|
||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">胜率</th>
|
||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">跟单人数</th>
|
||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">状态</th>
|
||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<?php if (!empty($plans)): ?>
|
||||
<?php foreach ($plans as $p): ?>
|
||||
<tr class="hover:bg-gray-50 transition-colors">
|
||||
<td class="px-4 py-3 text-sm text-gray-500"><?= $p['id'] ?></td>
|
||||
<td class="px-4 py-3 text-sm font-medium text-gray-900"><?= htmlspecialchars($p['name']) ?></td>
|
||||
<td class="px-4 py-3 text-sm text-center">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-primary/10 text-primary">
|
||||
<?= ['bs'=>'大小','oe'=>'单双','dt'=>'龙虎','sum_bs'=>'冠亚大小'][$p['plan_type']] ?? $p['plan_type'] ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-center"><?= $p['target_rank'] ?></td>
|
||||
<td class="px-4 py-3 text-sm text-right"><?= number_format($p['bet_amount'], 2) ?></td>
|
||||
<td class="px-4 py-3 text-sm text-center"><?= $p['total_records'] ?></td>
|
||||
<td class="px-4 py-3 text-sm text-center">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs <?= $p['win_rate'] >= 50 ? 'bg-success/10 text-success' : 'bg-danger/10 text-danger' ?>">
|
||||
<?= $p['win_rate'] ?>%
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-center font-semibold"><?= $p['follower_count'] ?></td>
|
||||
<td class="px-4 py-3 text-sm text-center">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs cursor-pointer <?= $p['status'] ? 'bg-success/10 text-success' : 'bg-gray-200 text-gray-500' ?>"
|
||||
onclick="togglePlan(<?= $p['id'] ?>)">
|
||||
<?= $p['status'] ? '启用' : '禁用' ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-center">
|
||||
<button onclick='editPlan(<?= json_encode($p) ?>)' class="text-primary hover:underline text-xs mr-2">编辑</button>
|
||||
<button onclick="deletePlan(<?= $p['id'] ?>)" class="text-danger hover:underline text-xs">删除</button>
|
||||
</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-clipboard-list 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>
|
||||
var planTypes = {bs:'大小', oe:'单双', dt:'龙虎', sum_bs:'冠亚大小'};
|
||||
|
||||
function showAddForm(data) {
|
||||
var isEdit = data && data.id;
|
||||
var html = '<div style="padding:20px">' +
|
||||
'<div style="margin-bottom:12px"><label style="display:block;margin-bottom:4px;font-size:13px;color:#666">计划名称</label>' +
|
||||
'<input type="text" id="fp_name" value="' + (data ? data.name : '') + '" class="layui-input" style="width:100%"></div>' +
|
||||
'<div style="display:flex;gap:12px;margin-bottom:12px">' +
|
||||
'<div style="flex:1"><label style="display:block;margin-bottom:4px;font-size:13px;color:#666">类型</label>' +
|
||||
'<select id="fp_type" class="layui-input">' +
|
||||
'<option value="bs"' + (data && data.plan_type === 'bs' ? ' selected' : '') + '>大小</option>' +
|
||||
'<option value="oe"' + (data && data.plan_type === 'oe' ? ' selected' : '') + '>单双</option>' +
|
||||
'<option value="dt"' + (data && data.plan_type === 'dt' ? ' selected' : '') + '>龙虎</option>' +
|
||||
'<option value="sum_bs"' + (data && data.plan_type === 'sum_bs' ? ' selected' : '') + '>冠亚大小</option>' +
|
||||
'</select></div>' +
|
||||
'<div style="flex:1"><label style="display:block;margin-bottom:4px;font-size:13px;color:#666">目标名次</label>' +
|
||||
'<input type="number" id="fp_rank" min="1" max="10" value="' + (data ? data.target_rank : 1) + '" class="layui-input"></div>' +
|
||||
'</div>' +
|
||||
'<div style="margin-bottom:12px"><label style="display:block;margin-bottom:4px;font-size:13px;color:#666">建议投注金额</label>' +
|
||||
'<input type="number" id="fp_amount" value="' + (data ? data.bet_amount : 100) + '" class="layui-input"></div>' +
|
||||
'</div>';
|
||||
|
||||
layui.use('layer', function() {
|
||||
layui.layer.open({
|
||||
type: 1, title: isEdit ? '编辑计划' : '新建计划',
|
||||
area: ['420px', '360px'], content: html,
|
||||
btn: ['保存', '取消'],
|
||||
yes: function(index) {
|
||||
var postData = {
|
||||
name: document.getElementById('fp_name').value,
|
||||
plan_type: document.getElementById('fp_type').value,
|
||||
target_rank: document.getElementById('fp_rank').value,
|
||||
bet_amount: document.getElementById('fp_amount').value,
|
||||
game_id: 1, status: 1
|
||||
};
|
||||
if (isEdit) postData.id = data.id;
|
||||
fetch('/admin/follow-plans/save', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify(postData)
|
||||
}).then(function(r) { return r.json(); }).then(function(d) {
|
||||
if (d.success) { layui.layer.close(index); location.reload(); }
|
||||
else { layui.layer.msg(d.message || '保存失败'); }
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function editPlan(plan) { showAddForm(plan); }
|
||||
|
||||
function togglePlan(id) {
|
||||
fetch('/admin/follow-plans/toggle', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({id: id})
|
||||
}).then(function(r) { return r.json(); }).then(function(d) {
|
||||
if (d.success) location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function deletePlan(id) {
|
||||
layui.use('layer', function() {
|
||||
layui.layer.confirm('确定删除此计划?所有相关记录将被清除。', function(index) {
|
||||
fetch('/admin/follow-plans/delete', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({id: id})
|
||||
}).then(function(r) { return r.json(); }).then(function(d) {
|
||||
if (d.success) { layui.layer.close(index); location.reload(); }
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user