feat: 更新依赖与增强功能

- 在 package.json 和 package-lock.json 中新增 laravel-echo 和 pusher-js 依赖
- 在 API 模块中新增 draw 相关函数的导出
- 在 PlayerAppShell 组件中引入 PlayerBottomNav 以增强底部导航
- 在 HallScreen 组件中引入 HallDrawPanel 以展示当前期号
This commit is contained in:
2026-05-09 17:40:26 +08:00
parent 3ae2c0e7d1
commit 7e28cc154a
19 changed files with 1067 additions and 31 deletions

8
src/lib/format-gmt.ts Normal file
View File

@@ -0,0 +1,8 @@
/** 大厅倒计时等用到的 `mm:ss`,与展示用绝对时间互为补充;绝对时间见 {@link formatLotteryInstant} */
export function formatSecondsClock(total: number): string {
const s = Math.max(0, Math.floor(total));
const mm = String(Math.floor(s / 60)).padStart(2, "0");
const ss = String(s % 60).padStart(2, "0");
return `${mm}:${ss}`;
}

54
src/lib/lottery-echo.ts Normal file
View File

@@ -0,0 +1,54 @@
import Echo from "laravel-echo";
import Pusher from "pusher-js";
/** 需在浏览器挂载 PusherReverb 走 pusher-js 协议) */
function ensurePusherOnWindow(): void {
if (typeof window === "undefined") return;
(
window as unknown as {
Pusher: typeof Pusher;
}
).Pusher = Pusher;
}
let echoSingleton: Echo<"reverb"> | null = null;
/**
* NEXT_PUBLIC_REVERB_APP_KEY与 Laravel .env `REVERB_APP_KEY` 相同
* NEXT_PUBLIC_REVERB_HOST / PORT / SCHEME浏览器连 Reverb WebSocket通常 localhost:8080 + http/ws
*/
export function getLotteryEcho(): Echo<"reverb"> | null {
if (typeof window === "undefined") return null;
const key = process.env.NEXT_PUBLIC_REVERB_APP_KEY;
const host = process.env.NEXT_PUBLIC_REVERB_HOST;
if (!key?.length || !host?.length) {
return null;
}
if (!echoSingleton) {
ensurePusherOnWindow();
const port = Number(process.env.NEXT_PUBLIC_REVERB_PORT ?? 8080);
const scheme = process.env.NEXT_PUBLIC_REVERB_SCHEME ?? "http";
const forceTLS = scheme === "https";
echoSingleton = new Echo({
broadcaster: "reverb",
key,
wsHost: host,
wsPort: forceTLS ? 443 : port,
wssPort: forceTLS ? port : 443,
forceTLS,
enabledTransports: ["ws", "wss"],
});
}
return echoSingleton;
}
export function disconnectLotteryEcho(): void {
if (echoSingleton) {
echoSingleton.disconnect();
echoSingleton = null;
}
}

View File

@@ -0,0 +1,25 @@
function pad2(n: number): string {
return String(n).padStart(2, "0");
}
/**
* 将接口 ISO 时间串格式化为 **浏览器本地时区** 下的 `YYYY-MM-DD HH:mm:ss`
* 与后台 `lotteryadmin/src/lib/admin-datetime.ts` {@link formatAdminInstant} 行为一致。
*/
export function formatLotteryInstant(iso: string | null | undefined): string {
if (iso == null || iso === "") {
return "—";
}
const ms = Date.parse(iso);
if (Number.isNaN(ms)) {
return "—";
}
const date = new Date(ms);
const y = date.getFullYear();
const m = pad2(date.getMonth() + 1);
const d = pad2(date.getDate());
const h = pad2(date.getHours());
const min = pad2(date.getMinutes());
const s = pad2(date.getSeconds());
return `${y}-${m}-${d} ${h}:${min}:${s}`;
}