feat: 神秘箱子系统完整实现 + 婚姻状态弹窗 + 工具栏优化
## 新功能 - 神秘箱子系统(MysteryBox)完整实现: - 新增 MysteryBox / MysteryBoxClaim 模型及迁移文件 - DropMysteryBoxJob / ExpireMysteryBoxJob 队列作业 - MysteryBoxController(/mystery-box/status + /mystery-box/claim) - 支持三种类型:普通箱(500~2000金)/ 稀有箱(5000~20000金)/ 黑化箱(陷阱扣200~1000金) - 调度器自动投放 + 管理员手动投放 - CurrencySource 新增 MYSTERY_BOX / MYSTERY_BOX_TRAP 枚举 - 婚姻状态弹窗(工具栏「婚姻」按钮): - 工具栏「呼叫」改为「婚姻」,点击打开婚姻状态弹窗 - 动态渲染三种状态:单身 / 求婚中 / 已婚 - 被求婚方可直接「答应 / 婉拒」;已婚可申请离婚(含二次确认) ## 优化修复 - frame.blade.php:Alpine.js CDN 补加 defer,修复所有组件初始化报错 - scripts.blade.php:神秘箱子暗号主动拦截(不依赖轮询),领取成功后弹 chatDialog 展示结果,更新金币余额 - MysteryBoxController:claim() 时 change() 补传 room_id 记录来源房间 - 后台游戏管理页(game-configs):投放箱子按钮颜色修复;弹窗替换为 window.adminDialog - admin/layouts:新增全局 adminDialog 弹窗组件(替代原生 alert/confirm) - baccarat-panel:FAB 拖动重构为 Alpine.js baccaratFab() 组件,与 slotFab 一致 - GAMES_TODO.md:神秘箱子移入已完成区,补全修复记录
This commit is contained in:
@@ -100,6 +100,31 @@
|
||||
<span class="text-xs text-gray-400">修改后立即生效(缓存60秒刷新)</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- 神秘箱子:手动投放区域 --}}
|
||||
@if ($game->game_key === 'mystery_box')
|
||||
<div class="mt-4 pt-4 border-t border-gray-100">
|
||||
<div class="text-xs font-bold text-gray-600 mb-2">🎯 手动投放箱子</div>
|
||||
<div class="flex items-center gap-3 flex-wrap">
|
||||
<button onclick="dropBox('normal', {{ $game->id }})"
|
||||
style="padding:8px 16px; background:linear-gradient(135deg,#059669,#10b981); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
|
||||
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
|
||||
📦 投放普通箱
|
||||
</button>
|
||||
<button onclick="dropBox('rare', {{ $game->id }})"
|
||||
style="padding:8px 16px; background:linear-gradient(135deg,#7c3aed,#a78bfa); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
|
||||
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
|
||||
💎 投放稀有箱
|
||||
</button>
|
||||
<button onclick="dropBox('trap', {{ $game->id }})"
|
||||
style="padding:8px 16px; background:linear-gradient(135deg,#7f1d1d,#ef4444); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:700; cursor:pointer; transition:opacity .15s;"
|
||||
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
|
||||
☠️ 投放黑化箱
|
||||
</button>
|
||||
<span class="text-xs text-gray-400">直接向 #1 房间投放,立即广播暗号</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@@ -153,10 +178,58 @@
|
||||
header.classList.toggle('bg-gray-50', !enabled);
|
||||
}
|
||||
|
||||
// Toast 提示
|
||||
alert(data.message);
|
||||
// 全局弹窗提示
|
||||
window.adminDialog.alert(data.message, enabled ? '游戏已开启' : '游戏已关闭', enabled ? '✅' : '⏸');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员手动投放神秘箱子
|
||||
*
|
||||
* @param {string} boxType 箱子类型:normal | rare | trap
|
||||
*/
|
||||
function dropBox(boxType) {
|
||||
const typeNames = {
|
||||
normal: '普通箱',
|
||||
rare: '稀有箱',
|
||||
trap: '黑化箱'
|
||||
};
|
||||
const typeIcons = {
|
||||
normal: '📦',
|
||||
rare: '💎',
|
||||
trap: '☠️'
|
||||
};
|
||||
const name = typeNames[boxType] || boxType;
|
||||
const icon = typeIcons[boxType] || '📦';
|
||||
|
||||
window.adminDialog.confirm(
|
||||
`确定要向 <b>#1 房间</b> 投放一个「${name}」吗?<br><span style="color:#64748b; font-size:12px;">箱子投放后将立即在公屏广播暗号,用户限时领取。</span>`,
|
||||
`投放${name}`,
|
||||
() => {
|
||||
fetch('/admin/mystery-box/drop', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
box_type: boxType
|
||||
}),
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
window.adminDialog.alert(
|
||||
data.message || (data.ok ? '投放成功!' : '投放失败'),
|
||||
data.ok ? '投放成功' : '投放失败',
|
||||
data.ok ? icon : '❌'
|
||||
);
|
||||
})
|
||||
.catch(() => window.adminDialog.alert('网络错误,请重试', '网络错误', '🌐'));
|
||||
},
|
||||
icon
|
||||
);
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
|
||||
@@ -200,6 +273,14 @@
|
||||
'auto_drop_enabled' => ['label' => '自动定时投放', 'type' => 'boolean', 'unit' => ''],
|
||||
'auto_interval_hours' => ['label' => '自动投放间隔', 'type' => 'number', 'unit' => '小时', 'min' => 1],
|
||||
'claim_window_seconds' => ['label' => '领取窗口', 'type' => 'number', 'unit' => '秒', 'min' => 10],
|
||||
// 新键名
|
||||
'normal_reward_min' => ['label' => '普通箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
'normal_reward_max' => ['label' => '普通箱最高奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
'rare_reward_min' => ['label' => '稀有箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
'rare_reward_max' => ['label' => '稀有箱最高奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
'trap_penalty_min' => ['label' => '黑化箱最低惩罚', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
'trap_penalty_max' => ['label' => '黑化箱最高惩罚', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
// 旧键名兼容(数据库中已存在的旧配置)
|
||||
'min_reward' => ['label' => '普通箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
'max_reward' => ['label' => '普通箱最高奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
'rare_min_reward' => ['label' => '稀有箱最低奖励', 'type' => 'number', 'unit' => '金币', 'min' => 1],
|
||||
|
||||
Reference in New Issue
Block a user