170 lines
7.9 KiB
PHP
170 lines
7.9 KiB
PHP
<div class="p-6">
|
|
<h2 class="text-2xl font-bold mb-4">⏱️ 自动开期设置</h2>
|
|
|
|
<!-- 宝塔配置提示 -->
|
|
<div class="bg-blue-50 border border-blue-200 rounded-xl p-4 mb-6">
|
|
<h3 class="font-bold text-blue-700 mb-2">📋 宝塔面板定时任务配置</h3>
|
|
<div class="text-sm text-blue-600 space-y-1">
|
|
<p>1. 登录宝塔面板 → <b>计划任务</b></p>
|
|
<p>2. 任务类型: <b>Shell脚本</b></p>
|
|
<p>3. 任务名称: <b>PK10自动开期</b></p>
|
|
<p>4. 执行周期: <b>每N分钟 → 1分钟</b></p>
|
|
<p>5. 脚本内容:</p>
|
|
<pre class="bg-blue-100 p-2 rounded mt-1 text-xs overflow-x-auto select-all" id="cronCmd">cd <?=ROOT_PATH?> && /usr/bin/php cron/auto_period_task.php >> Storage/log/auto_period.log 2>&1</pre>
|
|
<button onclick="copyCmd()" class="mt-2 px-3 py-1 bg-blue-500 text-white rounded text-xs hover:bg-blue-600">📋 复制命令</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 全局控制 -->
|
|
<div class="flex items-center justify-between mb-6">
|
|
<div class="flex items-center gap-4">
|
|
<span class="text-gray-500">全局控制:</span>
|
|
<button onclick="toggleAll(1)" class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 text-sm">✅ 全部启用</button>
|
|
<button onclick="toggleAll(0)" class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 text-sm">🚫 全部关闭</button>
|
|
</div>
|
|
<button onclick="refreshStatus()" class="px-4 py-2 bg-gray-200 rounded hover:bg-gray-300 text-sm">🔄 刷新状态</button>
|
|
</div>
|
|
|
|
<!-- 游戏列表 -->
|
|
<div class="space-y-4 mb-8">
|
|
<?php foreach($games ?? [] as $game): ?>
|
|
<div class="bg-white rounded-xl p-5 shadow" id="game-<?=$game['id']?>">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-lg font-bold"><?=$game['name']?></span>
|
|
<span class="px-2 py-1 rounded text-xs bg-gray-100 text-gray-500"><?=$game['type']?></span>
|
|
<span class="px-2 py-1 rounded text-xs <?=($game['auto_period_enabled']??0)?'bg-green-100 text-green-700':'bg-red-100 text-red-700'?>" id="status-<?=$game['id']?>">
|
|
<?=($game['auto_period_enabled']??0)?'✅ 自动开期中':'🚫 已关闭'?>
|
|
</span>
|
|
</div>
|
|
<label class="relative inline-flex items-center cursor-pointer">
|
|
<input type="checkbox" class="sr-only peer" <?=($game['auto_period_enabled']??0)?'checked':''?> onchange="toggleGame(<?=$game['id']?>, this.checked)">
|
|
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:bg-green-500 after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all"></div>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<!-- 每期时长 -->
|
|
<div>
|
|
<label class="block text-sm text-gray-500 mb-1">每期时长</label>
|
|
<div class="flex items-center gap-2">
|
|
<input type="number" min="60" max="3600" step="30" value="<?=$game['period_duration']??300?>"
|
|
id="duration-<?=$game['id']?>" class="w-24 border rounded px-3 py-2 text-center">
|
|
<span class="text-gray-400 text-sm">秒</span>
|
|
<span class="text-gray-300 text-xs">( = <span id="durationMin-<?=$game['id']?>"><?=round(($game['period_duration']??300)/60, 1)?></span> 分钟)</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 封盘提前时间 -->
|
|
<div>
|
|
<label class="block text-sm text-gray-500 mb-1">结束前提前封盘</label>
|
|
<div class="flex items-center gap-2">
|
|
<input type="number" min="5" max="120" step="5" value="<?=$game['lock_before_end']??30?>"
|
|
id="lock-<?=$game['id']?>" class="w-24 border rounded px-3 py-2 text-center">
|
|
<span class="text-gray-400 text-sm">秒</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 flex justify-end">
|
|
<button onclick="saveGame(<?=$game['id']?>)" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 text-sm">💾 保存设置</button>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<!-- 运行日志 -->
|
|
<div class="bg-white rounded-xl shadow overflow-hidden">
|
|
<h3 class="font-bold px-4 py-3 bg-gray-50 border-b">📜 最近自动开期日志</h3>
|
|
<div class="max-h-96 overflow-y-auto">
|
|
<table class="w-full text-sm">
|
|
<thead class="bg-gray-50 sticky top-0"><tr>
|
|
<th class="px-4 py-2 text-left">时间</th>
|
|
<th class="px-4 py-2 text-left">游戏</th>
|
|
<th class="px-4 py-2 text-left">动作</th>
|
|
<th class="px-4 py-2 text-left">详情</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
<?php foreach($logs ?? [] as $log): ?>
|
|
<tr class="border-t hover:bg-gray-50">
|
|
<td class="px-4 py-2 text-gray-400 text-xs whitespace-nowrap"><?=$log['created_at']??''?></td>
|
|
<td class="px-4 py-2">G<?=$log['game_id']??0?></td>
|
|
<td class="px-4 py-2">
|
|
<span class="px-2 py-1 rounded text-xs
|
|
<?php
|
|
$act = $log['action'] ?? '';
|
|
echo match($act) {
|
|
'start' => 'bg-green-100 text-green-700',
|
|
'lock' => 'bg-orange-100 text-orange-700',
|
|
'draw' => 'bg-blue-100 text-blue-700',
|
|
'settle' => 'bg-purple-100 text-purple-700',
|
|
'error' => 'bg-red-100 text-red-700',
|
|
default => 'bg-gray-100 text-gray-700',
|
|
};
|
|
?>">
|
|
<?=match($act) { 'start'=>'开期', 'lock'=>'封盘', 'draw'=>'开奖', 'settle'=>'结算', 'error'=>'错误', default=>$act }?>
|
|
</span>
|
|
</td>
|
|
<td class="px-4 py-2 text-gray-500 text-xs"><?=htmlspecialchars($log['message']??'')?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php if(empty($logs)): ?>
|
|
<tr><td colspan="4" class="px-4 py-8 text-center text-gray-400">暂无日志记录。请先执行数据库迁移并配置宝塔定时任务。</td></tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function copyCmd(){
|
|
const t=document.getElementById('cronCmd').textContent;
|
|
navigator.clipboard.writeText(t).then(()=>alert('已复制到剪贴板')).catch(()=>{
|
|
const ta=document.createElement('textarea');ta.value=t;document.body.appendChild(ta);ta.select();document.execCommand('copy');ta.remove();alert('已复制');
|
|
});
|
|
}
|
|
|
|
async function toggleGame(gameId, enabled){
|
|
const r=await fetch('/admin/auto-period/update',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({game_id:gameId,auto_period_enabled:enabled?1:0})});
|
|
const d=await r.json();
|
|
if(d.success){
|
|
const el=document.getElementById('status-'+gameId);
|
|
el.textContent=enabled?'✅ 自动开期中':'🚫 已关闭';
|
|
el.className='px-2 py-1 rounded text-xs '+(enabled?'bg-green-100 text-green-700':'bg-red-100 text-red-700');
|
|
}else{alert(d.message||'操作失败');}
|
|
}
|
|
|
|
async function saveGame(gameId){
|
|
const duration=parseInt(document.getElementById('duration-'+gameId).value)||300;
|
|
const lock=parseInt(document.getElementById('lock-'+gameId).value)||30;
|
|
const r=await fetch('/admin/auto-period/update',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({game_id:gameId,period_duration:duration,lock_before_end:lock})});
|
|
const d=await r.json();
|
|
if(d.success){
|
|
document.getElementById('durationMin-'+gameId).textContent=(duration/60).toFixed(1);
|
|
alert('保存成功');
|
|
}else{alert(d.message||'保存失败');}
|
|
}
|
|
|
|
async function toggleAll(enabled){
|
|
if(!confirm(enabled?'确认启用所有游戏的自动开期?':'确认关闭所有游戏的自动开期?'))return;
|
|
const r=await fetch('/admin/auto-period/toggle-all',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({enabled})});
|
|
const d=await r.json();
|
|
if(d.success){location.reload();}else{alert(d.message||'操作失败');}
|
|
}
|
|
|
|
async function refreshStatus(){
|
|
const r=await fetch('/admin/auto-period/status');
|
|
const d=await r.json();
|
|
if(d.success){location.reload();}
|
|
}
|
|
|
|
// 每期时长实时更新分钟显示
|
|
document.querySelectorAll('input[id^="duration-"]').forEach(el=>{
|
|
el.addEventListener('input',function(){
|
|
const gid=this.id.split('-')[1];
|
|
document.getElementById('durationMin-'+gid).textContent=(parseInt(this.value||300)/60).toFixed(1);
|
|
});
|
|
});
|
|
</script>
|