- 拆分 HomeController → TransactionController, ReportWebController, FollowPlanController - 新增 Service 层: TransactionService, ReportService, FollowPlanService - pk10.php JS 抽离为 4 个独立文件 (sound/race/bet/poll) - 前台新增报表查询页面 (/report) + 跟单计划页面 (/follow-plan) - 后台新增跟单计划管理 + 用户输赢明细统计 - 封盘状态显示倒计时 (x:xx) - 音效仅在开奖弹窗打开时播放 - 路由按模块分组整理 - autoload 支持 App\Services 命名空间
139 lines
7.0 KiB
PHP
139 lines
7.0 KiB
PHP
<?php use App\Core\I18n; I18n::init(); $t = function($k,$p=[]){return I18n::t($k,$p);}; ?>
|
|
<!DOCTYPE html><html lang="<?=I18n::getLang()?>">
|
|
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title><?=$t('report_query')?></title>
|
|
<link rel="stylesheet" href="/Static/css/app.css">
|
|
<style>
|
|
.page-header{display:flex;align-items:center;padding:12px 16px;background:#fff;border-bottom:1px solid #F0F0F0;position:sticky;top:0;z-index:10}
|
|
.page-header .back{width:32px;height:32px;display:flex;align-items:center;justify-content:center;cursor:pointer}
|
|
.page-header .title{flex:1;text-align:center;font-size:17px;font-weight:600;color:#333}
|
|
.page-header .balance{font-size:13px;color:#1E90FF;display:flex;align-items:center;gap:4px}
|
|
.date-tabs{display:flex;gap:0;background:#fff;padding:12px 16px 0;flex-wrap:wrap}
|
|
.date-tab{padding:6px 14px;font-size:13px;color:#666;cursor:pointer;border-bottom:2px solid transparent;background:none;border-top:none;border-left:none;border-right:none}
|
|
.date-tab.active{color:#1E90FF;border-bottom-color:#1E90FF;font-weight:600}
|
|
.report-table{width:100%;background:#fff;margin-top:1px}
|
|
.report-table th{font-size:12px;color:#999;font-weight:400;padding:10px 8px;text-align:center;border-bottom:1px solid #F0F0F0}
|
|
.report-table td{font-size:13px;color:#333;padding:10px 8px;text-align:center;border-bottom:1px solid #F5F5F5}
|
|
.report-table .win{color:#4CAF50;font-weight:600}
|
|
.report-table .lose{color:#FF4444;font-weight:600}
|
|
.empty-state{text-align:center;padding:60px 20px;color:#999;font-size:14px}
|
|
.summary-bar{display:flex;justify-content:space-around;background:#fff;padding:12px 16px;border-bottom:1px solid #F0F0F0}
|
|
.summary-item{text-align:center}
|
|
.summary-item .label{font-size:11px;color:#999}
|
|
.summary-item .value{font-size:16px;font-weight:700;margin-top:2px}
|
|
</style>
|
|
</head>
|
|
<body style="background:#F5F5F5;padding-bottom:20px">
|
|
|
|
<div class="page-header">
|
|
<a href="/profile" class="back"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg></a>
|
|
<div class="title"><?=$t('report_query')?></div>
|
|
<div class="balance"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v12M8 10h8M8 14h8"/></svg> <?=number_format($user['balance']??0,2)?></div>
|
|
</div>
|
|
|
|
<div class="date-tabs">
|
|
<button class="date-tab active" data-range="today"><?=$t('today')?></button>
|
|
<button class="date-tab" data-range="yesterday"><?=$t('yesterday')?></button>
|
|
<button class="date-tab" data-range="7days"><?=$t('last_7_days')?></button>
|
|
<button class="date-tab" data-range="30days"><?=$t('last_30_days')?></button>
|
|
<button class="date-tab" data-range="custom"><?=$t('custom_date')?></button>
|
|
</div>
|
|
|
|
<div id="customDateRow" style="display:none;background:#fff;padding:8px 16px;gap:8px;align-items:center">
|
|
<input type="date" id="dateFrom" style="flex:1;padding:6px 8px;border:1px solid #ddd;border-radius:6px;font-size:13px">
|
|
<span style="color:#999">-</span>
|
|
<input type="date" id="dateTo" style="flex:1;padding:6px 8px;border:1px solid #ddd;border-radius:6px;font-size:13px">
|
|
<button onclick="loadReport()" style="padding:6px 16px;background:#1E90FF;color:#fff;border:none;border-radius:6px;font-size:13px"><?=$t('search')?></button>
|
|
</div>
|
|
|
|
<div class="summary-bar">
|
|
<div class="summary-item">
|
|
<div class="label"><?=$t('bet_amount')?></div>
|
|
<div class="value" id="sumBet">0.00</div>
|
|
</div>
|
|
<div class="summary-item">
|
|
<div class="label"><?=$t('effective_flow')?></div>
|
|
<div class="value" id="sumFlow">0.00</div>
|
|
</div>
|
|
<div class="summary-item">
|
|
<div class="label"><?=$t('win_loss')?></div>
|
|
<div class="value" id="sumWinLoss" style="color:#333">0.00</div>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="report-table">
|
|
<thead>
|
|
<tr>
|
|
<th><?=$t('bet_type')?></th>
|
|
<th><?=$t('bet_amount')?></th>
|
|
<th><?=$t('amount')?></th>
|
|
<th><?=$t('effective_flow')?></th>
|
|
<th><?=$t('rebate_amount')?></th>
|
|
<th><?=$t('win_loss')?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="reportBody">
|
|
<tr><td colspan="6" class="empty-state"><?=$t('no_data')?></td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<script>
|
|
var currentRange='today';
|
|
document.querySelectorAll('.date-tab').forEach(function(btn){
|
|
btn.addEventListener('click',function(){
|
|
document.querySelectorAll('.date-tab').forEach(function(b){b.classList.remove('active');});
|
|
btn.classList.add('active');
|
|
currentRange=btn.dataset.range;
|
|
document.getElementById('customDateRow').style.display=currentRange==='custom'?'flex':'none';
|
|
if(currentRange!=='custom')loadReport();
|
|
});
|
|
});
|
|
|
|
function getDateRange(){
|
|
var now=new Date();
|
|
var fmt=function(d){var y=d.getFullYear(),m=('0'+(d.getMonth()+1)).slice(-2),dd=('0'+d.getDate()).slice(-2);return y+'-'+m+'-'+dd;};
|
|
if(currentRange==='today')return{from:fmt(now),to:fmt(now)};
|
|
if(currentRange==='yesterday'){var y=new Date(now);y.setDate(y.getDate()-1);return{from:fmt(y),to:fmt(y)};}
|
|
if(currentRange==='7days'){var d7=new Date(now);d7.setDate(d7.getDate()-6);return{from:fmt(d7),to:fmt(now)};}
|
|
if(currentRange==='30days'){var d30=new Date(now);d30.setDate(d30.getDate()-29);return{from:fmt(d30),to:fmt(now)};}
|
|
return{from:document.getElementById('dateFrom').value,to:document.getElementById('dateTo').value};
|
|
}
|
|
|
|
function loadReport(){
|
|
var r=getDateRange();
|
|
if(!r.from||!r.to)return;
|
|
fetch('/api/user-report?from='+r.from+'&to='+r.to)
|
|
.then(function(res){return res.json();})
|
|
.then(function(d){
|
|
if(!d.success)return;
|
|
var body=document.getElementById('reportBody');
|
|
var sumBet=0,sumFlow=0,sumWL=0;
|
|
if(!d.data||!d.data.length){
|
|
body.innerHTML='<tr><td colspan="6" class="empty-state"><?=$t('no_data')?></td></tr>';
|
|
document.getElementById('sumBet').textContent='0.00';
|
|
document.getElementById('sumFlow').textContent='0.00';
|
|
document.getElementById('sumWinLoss').textContent='0.00';
|
|
return;
|
|
}
|
|
var typeMap={bs:'<?=$t('big')?>/<?=$t('small')?>',oe:'<?=$t('odd')?>/<?=$t('even')?>',dt:'<?=$t('dragon')?>/<?=$t('tiger')?>',rank:'<?=$t('rank_1_10')?>',sum:'<?=$t('sum')?>',sum_bs:'<?=$t('sum_bs_tab')?>'};
|
|
var html='';
|
|
d.data.forEach(function(row){
|
|
var wl=parseFloat(row.win_loss);
|
|
var typeName=typeMap[row.type]||row.type;
|
|
sumBet+=parseFloat(row.bet_count);
|
|
sumFlow+=parseFloat(row.effective_flow);
|
|
sumWL+=wl;
|
|
html+='<tr><td>'+typeName+'</td><td>'+row.bet_count+'</td><td>'+parseFloat(row.amount).toFixed(2)+'</td><td>'+parseFloat(row.effective_flow).toFixed(2)+'</td><td>'+parseFloat(row.rebate).toFixed(2)+'</td><td class="'+(wl>=0?'win':'lose')+'">'+wl.toFixed(2)+'</td></tr>';
|
|
});
|
|
body.innerHTML=html;
|
|
document.getElementById('sumBet').textContent=sumBet;
|
|
document.getElementById('sumFlow').textContent=sumFlow.toFixed(2);
|
|
var wlEl=document.getElementById('sumWinLoss');
|
|
wlEl.textContent=sumWL.toFixed(2);
|
|
wlEl.style.color=sumWL>=0?'#4CAF50':'#FF4444';
|
|
});
|
|
}
|
|
loadReport();
|
|
</script>
|
|
</body></html>
|