Files
pk10/App/Views/Admin/follow_plans.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

153 lines
8.7 KiB
PHP

<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>