2026-04-19 12:46:59 +08:00
|
|
|
|
# 羽毛球記分板
|
2026-04-15 16:55:54 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
這是一個使用 `Vite + React + TypeScript + Node.js` 製作的羽毛球記分板專案,提供手機優先的記分介面、歷史戰績、房間觀戰、語音播報、PWA 安裝,以及 Docker / NAS 部署方式。
|
2026-04-15 16:55:54 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
## 功能特色
|
2026-04-15 16:55:54 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 選隊伍頁面
|
2026-04-19 12:54:18 +08:00
|
|
|
|
- 可依指定日期從資料庫讀取分組資料。
|
2026-08-20 12:19:53 +08:00
|
|
|
|
- match-hub V2 場地排程資料(`battlecombination` 以場地為 key、每場地 8 局 × 2 隊)會顯示成「2 號場地」「3 號場地」各 8 局賽程,挑一個場地帶進記分板;選隊面板的預設隊伍也會照局分組。
|
|
|
|
|
|
- 舊的 V1 三輪資料(personnel 含 B 區)仍照「第 N 組」顯示,兩種格式自動判別。
|
|
|
|
|
|
- 若當天沒有資料,可手動輸入「今日出席名單」排賽程:演算法與 match-hub V2 相同,依 `tg_members.skill` 實力(`GET /api/skills`,表名可用 `DB_MEMBERS_TABLE` 覆寫)做強弱互補與兩隊平衡,搭檔在全部組合輪過一遍前不重複、每人局數落差最多 1 局;8 人以上排 2、3 號兩個場地,未滿 8 人只排 2 號場地。
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 點進記分板時會直接帶入該組對戰。
|
2026-04-19 12:46:59 +08:00
|
|
|
|
- 記分板
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 兩隊隊員可自由交換上下、左右位置。
|
|
|
|
|
|
- 畫面編號固定為左上 `1`、右上 `2`、右下 `3`、左下 `4`。
|
|
|
|
|
|
- 先攻只能在開局設定一次,之後不會跟著發球權改變。
|
|
|
|
|
|
- 點擊分數直接加分,沒有加一減一按鈕。
|
|
|
|
|
|
- 第一分開始後,`設定隊伍` 會改成 `上一步`。
|
|
|
|
|
|
- `比賽結算` 需要長按 `1` 秒才會觸發,避免誤觸。
|
|
|
|
|
|
- 達標分數後有獲勝動畫與結算流程。
|
2026-04-28 18:17:41 +08:00
|
|
|
|
- 手機上會盡量壓縮成單頁滿版,避免上下滑動。
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 羽球規則
|
|
|
|
|
|
- 預設 `21` 分制,可在設定隊伍時調整目標分數。
|
|
|
|
|
|
- 支援 Deuce:`20:20` 後需領先 `2` 分才獲勝。
|
|
|
|
|
|
- `29:29` 時第 `30` 分直接獲勝。
|
|
|
|
|
|
- 發球方依羽球規則處理,`0` 分在右發球區。
|
|
|
|
|
|
- 畫面以下方隊伍為我方、上方隊伍為對方。
|
|
|
|
|
|
- 上方隊伍採鏡像顯示,所以我方 `0:0` 在右邊發球時,對方會在左邊接發。
|
2026-04-19 12:46:59 +08:00
|
|
|
|
- 語音播報
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 只在按下加分當下播報,不會因復原或其他操作重複報分。
|
|
|
|
|
|
- 可選擇是否播報得分與發球者。
|
|
|
|
|
|
- 同隊連續得分才會播報 `換邊發球`。
|
|
|
|
|
|
- 支援調整語速,最高可到 `10x`。
|
|
|
|
|
|
- `RURU` 會做大小寫無關判斷並以指定發音播報。
|
2026-04-16 19:57:08 +08:00
|
|
|
|
- 歷史戰績
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 可從資料庫讀取歷史列表。
|
|
|
|
|
|
- 點開單筆可查看得分過程。
|
|
|
|
|
|
- 每筆資料可刪除,刪除前會顯示確認提示。
|
2026-08-18 11:33:54 +08:00
|
|
|
|
- 今日場次
|
|
|
|
|
|
- 選隊伍面板每個人名字旁顯示 `今日 N 場`,每次結算幫上場的人各加一場,存在本機、跨日歸零。
|
2026-08-20 13:51:55 +08:00
|
|
|
|
- 結算上傳戰績時會檢查上場 4 人有沒有在當天 `attendance` 報名名單,忘了報名會自動補:有 TG 綁定(`tg_members`)就用真實 user_id,沒有就用名字 md5 產生的固定負數 id(與歷史匯入規則一致);補了誰會在畫面上提示。輪空等佔位字不補。
|
2026-08-18 11:33:54 +08:00
|
|
|
|
- 面板的 `同步今日場次` 按鈕會從 `history` 表撈今天(本機時區)的比賽,把每個人的今日場次覆蓋成 DB 的數字,跨裝置記分時用來補齊其他裝置上傳的場次。
|
2026-08-10 15:21:37 +08:00
|
|
|
|
- 出席率統計
|
|
|
|
|
|
- 歷史戰績頁的 `出席率統計` 按鈕會彈窗顯示每個人的出席狀況。
|
2026-08-17 13:50:59 +08:00
|
|
|
|
- 資料取自 `attendance` 表(TG 報名 bot 每天每人一列),不是 `history` 表,所以有報名到場但沒被記分的場次也算出席;當天名單還沒存進 `badminton` 表也已經算得到。
|
|
|
|
|
|
- 若 DB 沒有 `attendance` 表,會退回用 `badminton` 表的每日分組名單統計。表名可用 `DB_ATTENDANCE_TABLE` 覆寫。
|
2026-08-10 15:21:37 +08:00
|
|
|
|
- 同時顯示兩種出席率:
|
|
|
|
|
|
- `全期`:以資料庫全部場次為分母。
|
|
|
|
|
|
- `加入後`:從各自第一次出席那天算起,新加入的人不會被稀釋。
|
|
|
|
|
|
- 另外顯示最近 `12` 場的到場次數,可看出誰還在打球。
|
|
|
|
|
|
- 英文名大小寫不同(`RURU` / `RuRu` / `Ruru`)自動視為同一人;中文錯字用 `server/server.mjs` 的 `ATTENDANCE_ALIASES` 別名表對照,合併過的寫法會顯示在該列。
|
|
|
|
|
|
- `ATTENDANCE_IGNORED_NAMES` 列的佔位字(如 `輪空`)不列入統計。
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 房間觀戰
|
|
|
|
|
|
- 記分板帶入隊伍後會自動建立房間。
|
|
|
|
|
|
- 房間列表可查看目前直播中的比賽。
|
|
|
|
|
|
- 觀戰者只能看,不能操作記分。
|
|
|
|
|
|
- 分數、房間狀態、比賽結算會即時同步給觀戰者。
|
|
|
|
|
|
- 房間失效、重整、重選隊伍後也會通知觀戰者。
|
|
|
|
|
|
- 房間列表有 `重新取得列表`,並帶有 `5` 秒冷卻。
|
2026-04-16 19:57:08 +08:00
|
|
|
|
- PWA
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 可安裝到 iPhone / iPad / Android 主畫面。
|
|
|
|
|
|
- 支援 Web App 模式啟動。
|
|
|
|
|
|
- 新版本部署後會提示重新整理或重新安裝。
|
2026-04-15 23:15:52 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
## 本機開發
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-16 10:55:41 +08:00
|
|
|
|
### Port
|
|
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- Client: `3501`
|
|
|
|
|
|
- Server: `8788`
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
|
|
|
|
|
### 安裝
|
2026-04-15 16:55:54 +08:00
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
npm install
|
2026-04-15 22:56:50 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-19 12:46:59 +08:00
|
|
|
|
### 啟動開發模式
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-04-15 16:55:54 +08:00
|
|
|
|
npm run dev
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-19 12:46:59 +08:00
|
|
|
|
啟動後:
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-16 10:55:41 +08:00
|
|
|
|
- 前端:`http://localhost:3501`
|
|
|
|
|
|
- API:`http://localhost:8788`
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
### 檢查
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-16 10:19:23 +08:00
|
|
|
|
```bash
|
|
|
|
|
|
npm run lint
|
2026-04-16 10:55:41 +08:00
|
|
|
|
npm run build
|
2026-04-16 10:19:23 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-28 18:17:41 +08:00
|
|
|
|
## 記分板滿版模式
|
|
|
|
|
|
|
|
|
|
|
|
- 記分板頁面會套用 `100dvh` 高度。
|
|
|
|
|
|
- 手機進入記分板時會關閉頁面捲動與 overscroll。
|
|
|
|
|
|
- `viewport` 已加上 `viewport-fit=cover`,較能貼合 iPhone / iPad 安全區。
|
|
|
|
|
|
- 若手機高度較矮,會再縮小字級、按鈕與分數區,盡量維持整頁顯示。
|
|
|
|
|
|
|
2026-04-16 10:55:41 +08:00
|
|
|
|
## 環境變數
|
2026-04-16 10:19:23 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
請先建立 `.env`:
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
|
|
|
|
|
```env
|
2026-04-16 10:55:41 +08:00
|
|
|
|
DB_HOST=127.0.0.1
|
|
|
|
|
|
DB_PORT=3306
|
|
|
|
|
|
DB_USER=root
|
|
|
|
|
|
DB_PASSWORD=your_password
|
2026-04-15 22:56:50 +08:00
|
|
|
|
DB_DATABASE=badminton
|
2026-04-16 19:57:08 +08:00
|
|
|
|
DB_TABLE=badminton
|
2026-04-15 22:56:50 +08:00
|
|
|
|
DB_HISTORY_TABLE=history
|
2026-08-17 13:50:59 +08:00
|
|
|
|
DB_ATTENDANCE_TABLE=attendance
|
2026-04-16 10:55:41 +08:00
|
|
|
|
PORT=8788
|
2026-04-15 22:56:50 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-16 10:55:41 +08:00
|
|
|
|
## Docker / NAS 部署
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
對外服務配置:
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 容器內 Node / API:`8788`
|
|
|
|
|
|
- 對外 HTTPS 網址:`3501`
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-19 12:46:59 +08:00
|
|
|
|
部署指令:
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
sudo docker compose up -d --build
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
部署完成後可用:
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
https://你的網域或 NAS IP:3501
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
每次執行 `sudo docker compose up -d --build` 都會重新建置前後端與 PWA 靜態資產。
|
2026-04-16 20:35:31 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
## SSL 憑證
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
Docker Compose 會掛載以下目錄:
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
|
|
|
|
|
```text
|
2026-07-13 15:44:44 +08:00
|
|
|
|
/volume1/docker/certs/
|
2026-04-16 10:55:41 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
需包含:
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
2026-07-13 15:44:44 +08:00
|
|
|
|
- `cert.pem`
|
|
|
|
|
|
- `chain.pem`
|
|
|
|
|
|
- `privkey.pem`
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
2026-07-13 15:44:44 +08:00
|
|
|
|
nginx 容器會監看這個目錄,更新憑證檔案後會自動重新載入,不需要重啟容器。
|
|
|
|
|
|
|
|
|
|
|
|
### 憑證到期偵測
|
|
|
|
|
|
|
|
|
|
|
|
- 後端會讀取 `cert.pem` 的到期日,透過 `/api/version` 與 `/api/health` 回報給前端。
|
|
|
|
|
|
- 前端會把到期日存在 localStorage;當 API 出現 `Failed to fetch` 時,若存下來的到期日已過,會明確顯示「SSL 憑證已於某日過期」而不是通用錯誤。
|
|
|
|
|
|
- 憑證到期前 `7` 天,頁面會跳出提醒更新憑證的通知。
|
2026-04-16 10:55:41 +08:00
|
|
|
|
|
2026-04-19 12:46:59 +08:00
|
|
|
|
## 資料表格式
|
2026-04-16 19:57:08 +08:00
|
|
|
|
|
2026-04-19 12:46:59 +08:00
|
|
|
|
### `history`
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
|
|
|
|
|
- `id`
|
|
|
|
|
|
- `time`
|
|
|
|
|
|
- `dayOfWeek`
|
|
|
|
|
|
- `score`
|
|
|
|
|
|
- `winScore`
|
|
|
|
|
|
- `type`
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- `0`: 雙打
|
|
|
|
|
|
- `1`: 單打
|
2026-04-15 22:56:50 +08:00
|
|
|
|
- `players`
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 依照 `1 ~ 4` 固定編號順序儲存玩家名單。
|
2026-04-15 22:56:50 +08:00
|
|
|
|
- `team`
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- `1` 跟 `2` 一隊
|
|
|
|
|
|
- `3` 跟 `4` 一隊
|
2026-04-15 22:56:50 +08:00
|
|
|
|
- `scoreList`
|
2026-04-28 08:52:47 +08:00
|
|
|
|
- 格式:`[round, 發球者編號, 連勝數, 得分隊伍(0 或 1)]`
|
2026-04-15 22:56:50 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
## PWA Icon
|
2026-04-15 16:55:54 +08:00
|
|
|
|
|
2026-04-19 12:54:18 +08:00
|
|
|
|
目前使用:
|
2026-04-19 12:46:59 +08:00
|
|
|
|
|
|
|
|
|
|
- `public/favicon.png`
|
|
|
|
|
|
- `public/apple-touch-icon.png`
|
|
|
|
|
|
- `public/pwa-192.png`
|
|
|
|
|
|
- `public/pwa-512.png`
|
|
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
## Git 中文顯示
|
2026-04-19 12:46:59 +08:00
|
|
|
|
|
2026-04-28 08:52:47 +08:00
|
|
|
|
若要讓 git log / commit 顯示中文,建議設定:
|
2026-04-15 16:55:54 +08:00
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-04-16 10:55:41 +08:00
|
|
|
|
git config i18n.commitEncoding utf-8
|
|
|
|
|
|
git config i18n.logOutputEncoding utf-8
|
|
|
|
|
|
git config core.quotepath false
|
2026-04-15 16:55:54 +08:00
|
|
|
|
```
|