新增:双色球彩票前台 UI(阶段二)

🎟️ lottery-panel.blade.php 彩票面板
  - 红球 1~12(12宫格选3)/ 蓝球 1~6(骰子点数选1)
  - 购物车机制:可加入多注后一次性购买
  - 机选按钮(单注/3注)
  - 本期我的购票记录展示(含中奖标记)
  - 最近8期历史开奖号码表格
  - 规则折叠说明(奖级表格)
  - 停售/已开奖状态自动切换
  - 内联购票结果提示(3秒自动消失)

🎮 游戏大厅集成
  - game-hall 注入 lottery 开关状态
  - GAME_HALL_GAMES 追加双色球卡片(动态展示奖池/倒计时/超级期徽章)
  - frame.blade.php 引入 lottery-panel

🗺️ 路由 /games/enabled 已含 lottery 键
This commit is contained in:
2026-03-04 15:41:57 +08:00
parent a788a0022a
commit 4114571040
3 changed files with 662 additions and 0 deletions
+2
View File
@@ -147,6 +147,8 @@
@include('chat.partials.horse-race-panel')
{{-- ═══════════ 神秘占卜游戏面板 ═══════════ --}}
@include('chat.partials.fortune-panel')
{{-- ═══════════ 双色球彩票面板 ═══════════ --}}
@include('chat.partials.lottery-panel')
{{-- ═══════════ 娱乐游戏大厅弹窗 ═══════════ --}}
@include('chat.partials.game-hall')
@@ -22,6 +22,7 @@
'horse_racing' => \App\Models\GameConfig::isEnabled('horse_racing'),
'fortune_telling' => \App\Models\GameConfig::isEnabled('fortune_telling'),
'fishing' => \App\Models\GameConfig::isEnabled('fishing'),
'lottery' => \App\Models\GameConfig::isEnabled('lottery'),
];
@endphp
<script>
@@ -254,6 +255,47 @@
}),
btnLabel: () => '🎣 去钓鱼',
},
{
id: 'lottery',
name: '🎟️ 双色球',
desc: '每日20:00开奖,选3红1蓝,按奖池比例派奖,无一等奖滚存累积',
accentColor: '#dc2626',
fetchUrl: '/lottery/current',
openFn: () => {
closeGameHall();
if (typeof openLotteryPanel === 'function') openLotteryPanel();
},
renderStatus: (data) => {
if (!data?.issue) return {
badge: '⏸ 等待开期',
badgeStyle: 'background:#e8f0f8; color:#336699; border:1px solid #b8d0e8',
detail: '暂无进行中期次'
};
const iss = data.issue;
const pool = Number(iss.pool_amount || 0).toLocaleString();
if (data.is_open) {
const h = Math.floor(iss.seconds_left / 3600);
const m = Math.floor((iss.seconds_left % 3600) / 60);
const timeStr = h > 0 ? `${h}h ${m}m` : `${m}m`;
return {
badge: iss.is_super_issue ? '🎊 超级期购票中' : '🟢 购票中',
badgeStyle: 'background:#fef2f2; color:#b91c1c; border:1px solid #fca5a5',
detail: `💰 奖池 ${pool} 金 | 距开奖 ${timeStr} | 第 ${iss.issue_no} 期`
};
}
if (iss.status === 'settled') return {
badge: '✅ 已开奖',
badgeStyle: 'background:#d1fae5; color:#065f46; border:1px solid #6ee7b7',
detail: '本期已开奖,下期购票中'
};
return {
badge: '🔴 已停售',
badgeStyle: 'background:#fee2e2; color:#b91c1c; border:1px solid #fecaca',
detail: `💰 奖池 ${pool} 金 | 等待开奖中…`
};
},
btnLabel: (data) => data?.is_open ? '🎟️ 立即购票' : '📊 查看结果',
},
];
@@ -0,0 +1,618 @@
{{--
文件功能:双色球彩票前台弹窗组件
选号购票面板:
- 红球 1~12 选3 + 蓝球 1~6 选1,可机选
- 支持多注购买(单次最多10注)和追加购买
- 展示当期奖池、倒计时、本期已购记录
- 展示最近10期历史开奖号码
- 规则折叠说明卡片
@author ChatRoom Laravel
@version 1.0.0
--}}
{{-- ─── 彩票面板遮罩 ─── --}}
<div id="lottery-panel" x-data="lotteryPanel()" x-show="show" x-cloak
style="position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:9935;
display:flex; align-items:center; justify-content:center;">
<div x-transition:enter="transition ease-out duration-250" x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-180"
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95"
style="width:480px; max-width:96vw; max-height:92vh; border-radius:10px; overflow:hidden;
box-shadow:0 8px 32px rgba(0,0,0,.35); font-family:'Microsoft YaHei',SimSun,sans-serif;
background:#fff; display:flex; flex-direction:column;">
{{-- ─── 标题栏 ─── --}}
<div
style="background:linear-gradient(135deg,#dc2626,#ef4444); padding:10px 16px;
display:flex; align-items:center; justify-content:space-between; flex-shrink:0;">
<div>
<div style="color:#fff; font-weight:bold; font-size:14px;">🎟️ 双色球彩票</div>
<div style="color:rgba(255,255,255,.8); font-size:11px; margin-top:1px;">
<span x-text="issueNo">--</span> &nbsp;|&nbsp;
<span x-text="isOpen ? '🟢 购票中' : (status === 'settled' ? '✅ 已开奖' : '🔴 已停售')"></span>
</div>
</div>
<div style="text-align:right;">
{{-- 奖池金额 --}}
<div style="color:#fef08a; font-size:18px; font-weight:900; line-height:1;"
x-text="'💰 ' + poolAmount.toLocaleString()"></div>
{{-- 超级期标记 --}}
<div x-show="isSuperIssue" style="color:#fcd34d; font-size:10px; font-weight:bold; margin-top:2px;">🎊
超级期</div>
{{-- 距开奖倒计时 --}}
<div x-show="!isSuperIssue && isOpen"
style="color:rgba(255,255,255,.8); font-size:10px; margin-top:2px;" x-text="'距开奖 ' + countdownText">
</div>
</div>
<span onclick="closeLotteryPanel()"
style="cursor:pointer; font-size:20px; color:#fff; opacity:.8; line-height:1; margin-left:12px;"
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=.8">&times;</span>
</div>
{{-- ─── 内容区(可滚动)─── --}}
<div style="flex:1; overflow-y:auto; background:#fafafa;">
{{-- 加载中 --}}
<div x-show="loading" style="text-align:center; padding:40px 0; color:#dc2626; font-size:13px;">
<div style="font-size:28px; margin-bottom:8px;"></div>加载中…
</div>
<div x-show="!loading">
{{-- ─── 规则说明折叠区 ─── --}}
<div style="border-bottom:1px solid #fee2e2; background:#fff7f7;">
<div @click="ruleOpen=!ruleOpen"
style="padding:8px 14px; display:flex; align-items:center; justify-content:space-between;
cursor:pointer; font-size:12px; color:#b91c1c; font-weight:bold; user-select:none;">
<span>📖 玩法规则</span>
<span x-text="ruleOpen ? '▲ 收起' : '▼ 展开'" style="font-size:10px; opacity:.7;"></span>
</div>
<div x-show="ruleOpen" x-collapse
style="padding:0 14px 10px; font-size:11px; color:#6b7280; line-height:1.8;">
<div>🔴 <b>红球</b> 1~12 选3 · 🎲 <b>蓝球</b> 1~6 选1,每注 <b style="color:#dc2626;">100 金币</b></div>
<div>每期单人最多购 <b>50 </b>,可随时追加(停售前均可)</div>
<table style="width:100%; margin-top:6px; border-collapse:collapse;">
<tr style="background:#fee2e2;">
<th style="padding:3px 6px; text-align:left; font-size:10px;">奖级</th>
<th style="padding:3px 6px; text-align:left; font-size:10px;">条件</th>
<th style="padding:3px 6px; text-align:left; font-size:10px;">奖励</th>
</tr>
<tr>
<td style="padding:2px 6px;">🏆 一等奖</td>
<td>3+</td>
<td style="color:#dc2626;">奖池 60% 均分</td>
</tr>
<tr style="background:#fff7f7;">
<td style="padding:2px 6px;">🥇 二等奖</td>
<td>3</td>
<td style="color:#dc2626;">奖池 20% 均分</td>
</tr>
<tr>
<td style="padding:2px 6px;">🥈 三等奖</td>
<td>2+</td>
<td>奖池 10% 均分</td>
</tr>
<tr style="background:#fff7f7;">
<td style="padding:2px 6px;">🥉 四等奖</td>
<td>2</td>
<td>固定 150 /</td>
</tr>
<tr>
<td style="padding:2px 6px;">🎫 五等奖</td>
<td>1+</td>
<td>固定 50 /</td>
</tr>
</table>
<div style="margin-top:6px; color:#9ca3af;">无一等奖 奖池滚存至下期;连续3期无人中 超级期系统注入奖池</div>
</div>
</div>
{{-- 已开奖结果展示 --}}
<div x-show="status === 'settled' && drawRed1"
style="padding:10px 14px; background:#fff5f5; border-bottom:1px solid #fee2e2; text-align:center;">
<div style="font-size:12px; color:#b91c1c; font-weight:bold; margin-bottom:6px;">🎊 开奖结果</div>
<div style="display:flex; justify-content:center; gap:8px; align-items:center; flex-wrap:wrap;">
<template x-for="n in [drawRed1, drawRed2, drawRed3]" :key="n">
<span
style="width:32px; height:32px; border-radius:50%; background:#dc2626;
color:#fff; font-weight:bold; font-size:14px;
display:flex; align-items:center; justify-content:center;"
x-text="String(n).padStart(2,'0')"></span>
</template>
<span style="color:#6b7280; font-size:18px; font-weight:bold;">+</span>
<span
style="width:32px; height:32px; border-radius:50%; background:#2563eb;
color:#fff; font-weight:bold; font-size:14px;
display:flex; align-items:center; justify-content:center;"
x-text="drawBlue"></span>
</div>
</div>
{{-- ─── 选号区 ─── --}}
<div x-show="isOpen" style="padding:12px 14px;">
{{-- 红球选择 --}}
<div style="margin-bottom:10px;">
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">
🔴 红球(已选 <span x-text="selectedReds.length" style="color:#dc2626;"></span>/3
</div>
<div style="display:grid; grid-template-columns:repeat(6,1fr); gap:5px;">
<template x-for="n in 12" :key="n">
<button @click="toggleRed(n)"
:style="selectedReds.includes(n) ?
'background:#dc2626; color:#fff; border:2px solid #b91c1c; font-weight:bold;' :
'background:#fff; color:#dc2626; border:2px solid #fca5a5;'"
style="border-radius:50%; width:38px; height:38px; font-size:13px;
cursor:pointer; transition:all .15s; display:flex; align-items:center; justify-content:center;"
x-text="String(n).padStart(2,'0')">
</button>
</template>
</div>
</div>
{{-- 蓝球选择 --}}
<div style="margin-bottom:12px;">
<div style="font-size:12px; font-weight:bold; color:#1d4ed8; margin-bottom:6px;">
🎲 蓝球(骰子点数,已选 <span x-text="selectedBlue ?? '—'" style="color:#2563eb;"></span>
</div>
<div style="display:flex; gap:6px; flex-wrap:wrap;">
<template x-for="n in 6" :key="n">
<button @click="selectedBlue = (selectedBlue === n ? null : n)"
:style="selectedBlue === n ?
'background:#2563eb; color:#fff; border:2px solid #1d4ed8; font-weight:bold;' :
'background:#fff; color:#2563eb; border:2px solid #93c5fd;'"
style="border-radius:50%; width:38px; height:38px; font-size:16px;
cursor:pointer; transition:all .15s;"
x-text="['🎲','⚀','⚁','⚂','⚃','⚄','⚅'][n]">
</button>
</template>
</div>
</div>
{{-- 当前选号预览 --}}
<div x-show="selectedReds.length > 0 || selectedBlue"
style="background:#fff7f7; border:1px dashed #fca5a5; border-radius:8px;
padding:8px 12px; margin-bottom:10px; font-size:12px; color:#6b7280;">
已选:
<template x-for="n in [...selectedReds].sort((a,b)=>a-b)" :key="n">
<span
style="display:inline-block; width:26px; height:26px; border-radius:50%; background:#dc2626;
color:#fff; font-size:11px; font-weight:bold; text-align:center; line-height:26px; margin:0 2px;"
x-text="String(n).padStart(2,'0')"></span>
</template>
<span x-show="selectedBlue"
style="display:inline-block; width:26px; height:26px; border-radius:50%; background:#2563eb;
color:#fff; font-size:11px; font-weight:bold; text-align:center; line-height:26px; margin-left:6px;"
x-text="selectedBlue">
</span>
</div>
{{-- 操作按钮行(机选 + 清除 + 加入购物车 + 立即购买) --}}
<div style="display:flex; gap:6px; margin-bottom:10px;">
<button @click="doQuickPick(1)"
style="flex:1; padding:8px; background:#f3f4f6; border:1px solid #d1d5db;
border-radius:6px; font-size:12px; color:#374151; cursor:pointer;">
🎲 机选
</button>
<button @click="selectedReds=[]; selectedBlue=null;"
style="flex:1; padding:8px; background:#f3f4f6; border:1px solid #d1d5db;
border-radius:6px; font-size:12px; color:#374151; cursor:pointer;">
🗑 清除
</button>
<button @click="addToCart()"
style="flex:1; padding:8px; background:#fef3c7; border:1px solid #fde68a;
border-radius:6px; font-size:12px; color:#92400e; cursor:pointer; font-weight:bold;">
加入
</button>
</div>
{{-- 购物车(待购清单) --}}
<div x-show="cart.length > 0"
style="background:#fff7f7; border:1px solid #fecaca; border-radius:8px; padding:10px; margin-bottom:10px;">
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">
📋 待购清单(<span x-text="cart.length"></span> 注,
💰 <span x-text="cart.length * 100"></span> 金币)
</div>
<div style="max-height:100px; overflow-y:auto;">
<template x-for="(item, idx) in cart" :key="idx">
<div
style="display:flex; align-items:center; justify-content:space-between;
font-size:11px; color:#6b7280; padding:2px 0; border-bottom:1px dashed #fee2e2;">
<span>
<span x-text="idx+1"></span>
<template x-for="r in item.reds" :key="r">
<span
style="display:inline-block; width:20px; height:20px; border-radius:50%; background:#dc2626;
color:#fff; font-size:10px; text-align:center; line-height:20px; margin:0 1px;"
x-text="String(r).padStart(2,'0')"></span>
</template>
<span
style="display:inline-block; width:20px; height:20px; border-radius:50%; background:#2563eb;
color:#fff; font-size:10px; text-align:center; line-height:20px; margin-left:4px;"
x-text="item.blue"></span>
</span>
<button @click="cart.splice(idx,1)"
style="color:#dc2626; background:none; border:none; cursor:pointer; font-size:12px;"></button>
</div>
</template>
</div>
<button @click="submitCart()" :disabled="buying"
style="width:100%; margin-top:8px; padding:9px; background:linear-gradient(135deg,#dc2626,#ef4444);
color:#fff; border:none; border-radius:6px; font-size:13px; font-weight:bold;
cursor:pointer; transition:opacity .15s;"
:style="buying ? 'opacity:.6;cursor:not-allowed;' : ''">
<span
x-text="buying ? '购买中…' : '🎟️ 确认购买 ' + cart.length + ' 注(' + cart.length*100 + ' 金币)'"></span>
</button>
{{-- 内联购买结果提示 --}}
<div id="lottery-buy-msg"
style="display:none; border-radius:8px; padding:8px 12px; text-align:center;
margin-top:8px; font-size:12px; font-weight:bold;">
</div>
</div>
</div>
{{-- 停售/已开奖时的提示 --}}
<div x-show="!isOpen && status !== 'settled'"
style="padding:16px 14px; text-align:center; color:#b91c1c; font-size:13px;">
🔴 本期已停售,等待开奖中…
</div>
{{-- ─── 本期我的购票记录 ─── --}}
<div x-show="myTickets.length > 0" style="padding:10px 14px; border-top:1px solid #fee2e2;">
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">
📋 本期我的购票(<span x-text="myTickets.length"></span> 注)
</div>
<div style="max-height:120px; overflow-y:auto;">
<template x-for="(t, idx) in myTickets" :key="t.id">
<div
style="display:flex; align-items:center; justify-content:space-between;
font-size:11px; padding:4px 0; border-bottom:1px dashed #fee2e2;">
<div style="display:flex; align-items:center; gap:3px;">
<span style="color:#9ca3af; min-width:28px;" x-text="'注'+(idx+1)"></span>
<template x-for="r in [t.red1,t.red2,t.red3]" :key="r">
<span
style="width:20px; height:20px; border-radius:50%; background:#dc2626;
color:#fff; font-size:10px; text-align:center; line-height:20px;"
x-text="String(r).padStart(2,'0')"></span>
</template>
<span
style="width:20px; height:20px; border-radius:50%; background:#2563eb;
color:#fff; font-size:11px; text-align:center; line-height:20px; margin-left:2px;"
x-text="t.blue"></span>
<span x-show="t.is_quick" style="color:#9ca3af; font-size:10px;">机选</span>
</div>
{{-- 中奖标记 --}}
<span x-show="t.prize_level > 0"
style="color:#dc2626; font-weight:bold; font-size:11px;"
x-text="['','🏆一等','🥇二等','🥈三等','🥉四等','🎫五等'][t.prize_level] + ' +' + t.payout.toLocaleString()"></span>
<span x-show="t.prize_level === 0 && status === 'settled'"
style="color:#9ca3af; font-size:11px;">未中奖</span>
</div>
</template>
</div>
</div>
{{-- ─── 历史开奖记录 ─── --}}
<div style="padding:10px 14px; border-top:1px solid #fee2e2;">
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">📜 近期开奖</div>
<div x-show="history.length === 0"
style="font-size:11px; color:#9ca3af; text-align:center; padding:8px 0;">
暂无历史记录
</div>
<div style="overflow-x:auto;">
<table style="width:100%; font-size:10px; border-collapse:collapse; min-width:300px;">
<tr style="background:#fee2e2; color:#b91c1c;">
<th style="padding:3px 5px; text-align:left;">期号</th>
<th style="padding:3px 5px;">开奖号码</th>
<th style="padding:3px 5px; text-align:right;">奖池</th>
</tr>
<template x-for="(h, i) in history.slice(0,8)" :key="h.issue_no">
<tr :style="i % 2 === 0 ? '' : 'background:#fff7f7;'">
<td style="padding:3px 5px; color:#6b7280;" x-text="h.issue_no"></td>
<td style="padding:3px 5px; text-align:center;">
<template x-for="r in [h.red1,h.red2,h.red3]" :key="r">
<span
style="display:inline-block; width:18px; height:18px; border-radius:50%;
background:#dc2626; color:#fff; font-size:9px; font-weight:bold;
text-align:center; line-height:18px; margin:0 1px;"
x-text="String(r).padStart(2,'0')"></span>
</template>
<span
style="display:inline-block; width:18px; height:18px; border-radius:50%;
background:#2563eb; color:#fff; font-size:9px; font-weight:bold;
text-align:center; line-height:18px; margin-left:3px;"
x-text="h.blue"></span>
</td>
<td style="padding:3px 5px; text-align:right; color:#dc2626;"
x-text="'💰' + Number(h.pool_amount).toLocaleString()"></td>
</tr>
</template>
</table>
</div>
</div>
</div>{{-- /!loading --}}
</div>{{-- /内容区 --}}
{{-- ─── 底部操作栏 ─── --}}
<div
style="padding:8px 14px; background:#fff; border-top:1px solid #fee2e2;
display:flex; justify-content:space-between; align-items:center; flex-shrink:0;">
<button @click="doQuickPick(3)" x-show="isOpen"
style="padding:6px 14px; background:#fef3c7; border:1px solid #fde68a;
border-radius:6px; font-size:12px; color:#92400e; cursor:pointer;">
🎲 一次机选3注
</button>
<button @click="loadData()"
style="padding:6px 14px; background:#f3f4f6; border:1px solid #e5e7eb;
border-radius:6px; font-size:12px; color:#6b7280; cursor:pointer;">
🔄 刷新
</button>
<button @click="closeLotteryPanel()"
style="padding:6px 14px; background:#f0f6ff; border:1px solid #b0d0ee;
border-radius:6px; font-size:12px; color:#336699; cursor:pointer;">
关闭
</button>
</div>
</div>
</div>
<script>
/**
* 双色球彩票面板 Alpine.js 组件
*/
function lotteryPanel() {
return {
// ─── 状态 ───
show: false,
loading: true,
ruleOpen: false,
buying: false,
// ─── 期次数据 ───
issueNo: '--',
status: 'open',
isOpen: false,
isSuperIssue: false,
poolAmount: 0,
secondsLeft: 0,
drawAt: null,
drawRed1: null,
drawRed2: null,
drawRed3: null,
drawBlue: null,
// ─── 选号 ───
selectedReds: [],
selectedBlue: null,
cart: [], // 待购清单
// ─── 数据 ───
myTickets: [],
history: [],
// ─── 倒计时 ───
_timer: null,
/**
* 格式化倒计时文字(如 4h 22m 01:59
*/
get countdownText() {
const s = this.secondsLeft;
if (s <= 0) return '即将开奖';
if (s >= 3600) return `${Math.floor(s/3600)}h ${Math.floor((s%3600)/60)}m`;
const m = Math.floor(s / 60);
const sec = s % 60;
return `${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}`;
},
/**
* 打开面板并加载数据
*/
async open() {
this.show = true;
await this.loadData();
this.startTimer();
},
/**
* 加载当期状态、我的购票、历史开奖
*/
async loadData() {
this.loading = true;
try {
const [currentRes, histRes] = await Promise.all([
fetch('/lottery/current', {
headers: {
'Accept': 'application/json'
}
}),
fetch('/lottery/history', {
headers: {
'Accept': 'application/json'
}
}),
]);
const current = await currentRes.json();
const hist = await histRes.json();
if (current.issue) {
const iss = current.issue;
this.issueNo = iss.issue_no;
this.status = iss.status;
this.isOpen = current.is_open;
this.isSuperIssue = iss.is_super_issue;
this.poolAmount = iss.pool_amount;
this.secondsLeft = iss.seconds_left;
this.drawRed1 = iss.red1;
this.drawRed2 = iss.red2;
this.drawRed3 = iss.red3;
this.drawBlue = iss.blue;
}
this.myTickets = current.my_tickets ?? [];
this.history = hist.issues ?? [];
} catch (e) {
// 网络异常静默处理
}
this.loading = false;
},
/**
* 启动倒计时 ticker
*/
startTimer() {
clearInterval(this._timer);
this._timer = setInterval(() => {
if (this.secondsLeft > 0) {
this.secondsLeft--;
}
}, 1000);
},
/**
* 切换红球选择状态
*/
toggleRed(n) {
if (this.selectedReds.includes(n)) {
this.selectedReds = this.selectedReds.filter(r => r !== n);
} else if (this.selectedReds.length < 3) {
this.selectedReds = [...this.selectedReds, n];
}
},
/**
* 服务端机选号码(立即加入购物车)
*/
async doQuickPick(count = 1) {
try {
const res = await fetch(`/lottery/quick-pick?count=${count}`);
const data = await res.json();
for (const num of data.numbers) {
if (this.cart.length < 10) {
this.cart.push({
reds: num.reds,
blue: num.blue,
quick: true
});
}
}
// 清空当前选号
this.selectedReds = [];
this.selectedBlue = null;
} catch {}
},
/**
* 将当前选号加入购物车
*/
addToCart() {
if (this.selectedReds.length !== 3 || !this.selectedBlue) {
showLotteryMsg('⚠️ 请选满 3 个红球和 1 个蓝球', false);
return;
}
if (this.cart.length >= 10) {
showLotteryMsg('⚠️ 单次最多加入 10 注', false);
return;
}
this.cart.push({
reds: [...this.selectedReds].sort((a, b) => a - b),
blue: this.selectedBlue,
quick: false
});
// 清空选号等待下一注
this.selectedReds = [];
this.selectedBlue = null;
},
/**
* 提交购物车(批量购买)
*/
async submitCart() {
if (this.cart.length === 0 || this.buying) return;
this.buying = true;
try {
const res = await fetch('/lottery/buy', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
numbers: this.cart.map(c => ({
reds: c.reds,
blue: c.blue
})),
quick_pick: false,
}),
});
const data = await res.json();
if (res.ok && data.status === 'success') {
showLotteryMsg('✅ ' + data.message, true);
this.cart = [];
// 更新金币余额显示
if (window.chatContext) {
window.chatContext.userJjb = Math.max(0, (window.chatContext.userJjb ?? 0) - data
.count * 100);
}
// 刷新我的购票列表
await this.loadData();
} else {
showLotteryMsg('❌ ' + (data.message || '购票失败'), false);
}
} catch {
showLotteryMsg('🌐 网络异常,请稍后重试', false);
}
this.buying = false;
},
};
}
/**
* 显示彩票面板内联消息(3s 后自动消失)
*
* @param {string} msg
* @param {boolean} success
*/
function showLotteryMsg(msg, success) {
const el = document.getElementById('lottery-buy-msg');
if (!el) return;
el.style.background = success ? '#f0fdf4' : '#fff5f5';
el.style.border = success ? '1px solid #86efac' : '1px solid #fecaca';
el.style.color = success ? '#16a34a' : '#dc2626';
el.textContent = msg;
el.style.display = 'block';
el.style.opacity = '1';
clearTimeout(el._t);
el._t = setTimeout(() => {
el.style.opacity = '0';
setTimeout(() => {
el.style.display = 'none';
}, 400);
}, 3000);
}
/** 打开彩票面板 */
window.openLotteryPanel = function() {
const panel = document.getElementById('lottery-panel');
if (panel) Alpine.$data(panel).open();
};
/** 关闭彩票面板 */
window.closeLotteryPanel = function() {
const panel = document.getElementById('lottery-panel');
if (panel) {
const data = Alpine.$data(panel);
data.show = false;
clearInterval(data._timer);
}
};
</script>