一個現代化的多語言數位菜單系統,為 活力園 (Stamina-en) 餐廳打造。具備先進的 TTS 語音播放、多層級快取架構、PWA 離線支援和響應式設計,提供卓越的用戶體驗。
- 🌍 多語言支援 - 日文、繁體中文、簡體中文、英文
- 🔊 智能 TTS 語音 - 日文菜單項目語音播放,支援多層級快取
- 🎵 數量選擇播放 - 1-9 數量選擇,分離式連續語音播放
- 🎶 按鈕音效系統 - + / - 按鈕點擊音效,到達邊界時特殊提示音
- ⚡ 預下載優化 - 背景預下載數量音檔,播放延遲減少 52%
- 🛒 智能購物車系統 - 完整的購物車功能,含多語系支援和視覺化狀態
- 📱 響應式設計 - 完美適配手機、平板、桌面端
- 🍽️ 桌號 QR Code 系統 - 自動桌號識別,提升服務效率
- 🚀 極速載入 - 客戶端直接存取 R2,零中轉延遲
- 🎨 字體優化 - 字體子集化,減少 70% 載入時間
- 🔄 離線支援 - PWA 技術,支援離線瀏覽
- 📊 高效能 - Core Web Vitals 優化,Lighthouse 95+ 分
Frontend:
├── Next.js 15.1.2 (React 18.2.0)
├── TypeScript 5.7.2
├── Tailwind CSS 3.4.17
├── Shadcn/UI Components
└── Zustand (狀態管理)
Backend & Services:
├── Next.js API Routes
├── Azure Text-to-Speech API
└── Cloudflare R2 (唯一快取)
PWA & Caching:
├── Service Worker (Workbox)
├── 客戶端直接 R2 存取
├── API 智能回退機制
├── 數量音檔預下載
├── 背景同步
└── 離線支援┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Service Worker │────│ Cloudflare R2 │────│ Vercel API │
│ (超強快取) │ │ (直接存取) │ │ (回退處理) │
│ │ │ │ │ │
│ • R2 音訊: 1年 │ │ • 直接 GET 請求 │ │ • 檔案不存在時 │
│ • API 快取: 90天 │ │ • 零中轉延遲 │ │ • Azure TTS 生成 │
│ • 持久性存儲 │ │ • 全球 CDN │ │ • 檔案上傳到 R2 │
│ • 1000檔案容量 │ │ • 1年 TTL │ │ • 回傳音訊內容 │
│ • 毫秒級響應 │ │ • CORS 優化 │ │ • 智能回退機制 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
- Node.js 18.0+
- npm 8.0+
- 支援 ES2022 的瀏覽器
- 克隆專案
git clone https://github.com/your-org/stamina-en-menu.git
cd stamina-en-menu- 安裝依賴
npm install- 環境變數設定
# 複製環境變數模板
cp .env.example .env.local
# 設定必要的環境變數
AZURE_SPEECH_KEY=your_azure_speech_key
AZURE_SPEECH_REGION=your_azure_region
CLOUDFLARE_R2_ACCESS_KEY_ID=your_r2_access_key
CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_r2_secret_key
CLOUDFLARE_R2_BUCKET_NAME=your_bucket_name
CLOUDFLARE_R2_PUBLIC_URL=https://your-domain.com
NEXT_PUBLIC_CLOUDFLARE_R2_PUBLIC_URL=https://your-domain.com- 生成字體子集
npm run generate-subsets- 啟動開發伺服器
npm run dev應用程式將在 http://localhost:3000 啟動。
npm run dev # 啟動開發伺服器
npm run dev-https # 啟動 HTTPS 開發伺服器
npm run build # 建置生產版本
npm run start # 啟動生產伺服器npm run lint # 執行 ESLint 檢查
npm run generate-subsets # 生成字體子集檔案# 預熱 TTS 快取(建議部署後執行)
curl -X POST https://your-domain.com/api/tts/prewarm
# 檢查預熱狀態
curl https://your-domain.com/api/tts/prewarmsrc/
├── components/ # React 組件
│ ├── ui/ # Shadcn UI 組件
│ ├── Menu.tsx # 主菜單組件 (含數量選擇器)
│ ├── CartDrawer.tsx # 購物車側邊彈窗組件
│ ├── FontWrapper.tsx # 字體載入組件
│ └── ...
├── pages/ # Next.js 頁面
│ ├── api/ # API 路由
│ │ ├── menu.ts # 菜單資料 API
│ │ └── tts/ # TTS 相關 API
│ │ ├── [text].ts # 文字轉語音
│ │ ├── prewarm.ts # 預熱快取
│ │ └── health.ts # 健康檢查
│ ├── admin/ # 管理功能頁面
│ │ └── qrcode.tsx # QR Code 生成器
│ ├── _app.tsx # 應用程式根組件
│ └── index.tsx # 首頁
├── store/ # Zustand 狀態管理
│ ├── languageStore.ts
│ └── cartStore.ts # 購物車狀態管理
├── types/ # TypeScript 類型定義
│ ├── menu.ts # 菜單相關類型
│ └── cart.ts # 購物車相關類型
├── config/ # 配置檔案
│ ├── fonts.ts # 字體配置
│ └── translations.ts # 多語系翻譯
├── lib/ # 工具函數
│ ├── audioStreaming.ts # 音訊串流
│ ├── cacheMetrics.ts # 快取指標
│ └── ...
├── data/ # 資料檔案
│ └── data.json # 菜單資料
└── styles/ # 全域樣式
└── globals.css
GET /api/menu # 獲取菜單資料GET /api/tts/[text] # 文字轉語音
POST /api/tts/prewarm # 預熱常用項目
GET /api/tts/prewarm # 查詢預熱狀態
GET /api/tts/health # TTS 服務健康檢查- 更新
src/types/menu.ts中的Language型別 - 編輯
src/data/data.json新增翻譯內容 - 更新
src/config/fonts.ts字體配置 - 調整
src/store/languageStore.ts語言順序
編輯 src/data/data.json 檔案,確保所有語言都有對應翻譯:
{
"menuItems": [
{
"id": "item1",
"translations": {
"ja": "上ロース",
"zh-tw": "上級里脊",
"zh-cn": "上级里脊",
"en": "Premium Loin"
},
"price": 1200,
"category": "meat"
}
]
}- 首次內容繪製 (FCP): < 1.5s
- 最大內容繪製 (LCP): < 2.5s
- 首次輸入延遲 (FID): < 100ms
- 累積版面配置偏移 (CLS): < 0.1
- TTS 響應時間: < 3s (95% 情況)
- Service Worker 快取: 毫秒級響應 (< 50ms)
- R2 音訊檔案:1年超長期快取
- API 響應:90天智能快取
- 持久性存儲保護,不易被清理
- R2 直接存取: 零中轉延遲 (< 200ms)
- API 回退處理: 檔案不存在時生成 (< 500ms)
- 預熱機制: 80+ 常用項目預生成
- 智能快取: 支援 1000+ 音訊檔案永久快取
- 連接 GitHub 儲存庫到 Vercel
- 設定環境變數
- 部署並執行預熱:
curl -X POST https://your-app.vercel.app/api/tts/prewarm支援任何 Node.js 託管平台:
- Netlify
- Railway
- Render
- 自託管 (Docker)
# 系統健康檢查
curl https://your-domain.com/api/health
# TTS 服務檢查
curl https://your-domain.com/api/tts/health- 使用 Vercel Analytics 監控 Core Web Vitals
- 追蹤 TTS API 使用量和響應時間
- 監控快取命中率和錯誤率
// 查看快取狀態
navigator.serviceWorker.controller?.postMessage({
type: 'CACHE_MANAGEMENT',
action: 'GET_CACHE_INFO'
});
// 清理 API 快取
navigator.serviceWorker.controller?.postMessage({
type: 'CACHE_MANAGEMENT',
action: 'CLEAR_CACHE'
});
// 清理 R2 音訊快取
navigator.serviceWorker.controller?.postMessage({
type: 'CACHE_MANAGEMENT',
action: 'CLEAR_R2_CACHE'
});
// 檢查持久性存儲
navigator.storage.estimate().then(estimate => {
console.log('存儲使用情況:', estimate);
});# 更新版本號(自動觸發 PWA 更新)
npm version patch # 0.2.2 → 0.2.3
npm version minor # 0.2.2 → 0.3.0
npm version major # 0.2.2 → 1.0.0
# 構建時自動注入版本號
npm run build
# 部署後用戶會收到 PWA 更新提示版本自動化:
- 📦 package.json: 主版本源
- 🔄 Service Worker: 自動讀取版本號
- 📱 PWA: 版本變更時自動提示更新
- ⏰ 檢查間隔: 每 24 小時自動檢查更新
- HTTPS 強制: 所有連線均使用 HTTPS
- API 金鑰保護: 敏感資訊僅在伺服器端處理
- CORS 配置: 適當的跨域資源共享設定
- 輸入驗證: 防止 XSS 和注入攻擊
- 快取安全: 適當的快取標頭和過期策略
TTS 音訊播放失敗
- 檢查 Azure Speech Service 配置
- 確認 API 金鑰有效性
- 檢查網路連線狀態
字體載入失敗
- 重新生成字體子集:
npm run generate-subsets - 檢查字體檔案路徑
- 確認 CDN 設定正確
快取問題
- 清理瀏覽器快取
- 重新註冊 Service Worker
- 檢查快取策略設定
Cloudflare R2 CORS 問題
- 症狀: 特定音訊檔案需要多次連線,瀏覽器 CORS 錯誤,但 curl 測試正常
- 根本原因: Cloudflare CDN 快取了沒有 CORS 標頭的舊版本檔案
- 解決方案:
- 進入 Cloudflare 控制台 → Caching → Configuration → Purge Cache
- 選擇 "Custom Purge" → "Purge by URL"
- 輸入問題檔案的完整 URL (例:
https://tts-cache.36.to/檔案名.mp3) - 點擊 "Purge" 清除快取
- 等待 5-10 分鐘讓變更生效
- 測試瀏覽器音訊播放是否恢復正常
- 預防措施:
- CORS 設定變更後,主動清除相關檔案的 CDN 快取
- 監控
cf-cache-status標頭,確認是否從快取返回 - 定期檢查特定檔案的 CORS 標頭是否正確
# 檢查建置輸出
npm run build -- --debug
# TypeScript 類型檢查
npx tsc --noEmit
# 查看詳細日誌
DEBUG=* npm run dev- 🍽️ 桌號 QR Code 點餐系統 - 完整的桌號管理功能
- 📋 QR Code 生成器 - 位於
/admin/qrcode,支援批量生成、列印功能 - 🎯 自動桌號識別 - 透過 URL 參數自動讀取桌號 (
?table=A01) - 🛒 購物車桌號顯示 - 在購物車標題中顯示對應桌號
- 💾 桌號持久化 - 使用 localStorage 保存桌號資訊
- 🖨️ 並排列印設計 - QR Code 並排列印,不延伸到頁面邊界
- 🎨 購物車 UI 優化 - 桌號與標題同行顯示,更加緊湊
- 🛒 完整購物車系統 - 全新的購物車功能,支援商品加入、數量調整、移除操作
- 🌍 購物車多語系支援 - 購物車界面完全支援四種語言(日文、繁中、簡中、英文)
- 🎨 菜品狀態視覺化 - 在菜品列表中高亮顯示已加入購物車的商品
- 🔢 數量徽章顯示 - 在菜品名稱旁顯示購物車中的數量徽章
- 💾 持久化存儲 - 使用 localStorage 保存購物車內容,重新載入不會遺失
- 💰 智能金額計算 - 自動計算小計、消費稅(10%)和總金額
- 🎯 優雅視覺設計 - 淡灰色高亮效果,不會過於突兀但清楚易辨識
- 📱 雙重操作路徑 - 可從主頁面或菜品詳情頁開啟購物車,關閉時智能返回
- 🌙 暗黑模式支援 - 購物車組件完全適配暗黑模式顯示
- 📱 改善彈窗使用體驗 - 優化手機端彈窗操作流程
- 🎯 分類標籤居中 - 將"燒肉"等分類標籤從左上角移至居中顯示
- 📐 緊湊化設計 - 減少內容項目上下間距,讓彈窗更緊湊
- ↩️ 返回按鈕優化 - 移至左上角並改為返回箭頭圖示,更直觀明顯
- 🎨 視覺層次改善 - 調整字體大小、間距和按鈕尺寸
- 📱 多設備適配 - 確保在更多手機裝置上完整顯示內容
- 🎶 新增數量選擇器音效 - + / - 按鈕點擊音效,提升用戶體驗
- 🔊 智能音效系統 - 不同按鈕使用不同頻率音效
- 🎯 邊界提示音 - 到達最低值(1)或最高值(9)時播放特殊提示音
- 🛠️ Web Audio API - 純前端音效生成,無需額外音頻檔案
- 🎨 音效優化 - 短促音效設計,不干擾主要功能
- 🎵 新增數量選擇器功能 - 支援 1-9 數量選擇
- ⚡ 音訊播放優化 - 分離式播放策略,重複使用既有音檔
- 🚀 預下載機制 - 背景預下載數量音檔,播放延遲減少 52%
- 🎯 智能播放策略 - 優先使用預下載音檔,回退至 TTS API
- 💾 記憶體管理 - 自動管理 blob URL 生命週期
- 🎨 UI/UX 改進 - 平衡的視覺設計,清晰的操作回饋
- ⚡ 實現音訊檔案超長期快取策略
- 🔒 新增 R2 音訊檔案 1年本地快取
- 💾 支援最多 1000 個音訊檔案永久快取
- 🛡️ 請求持久性存儲權限,防止自動清理
- 📊 增強快取管理:分別統計 API/R2 快取
- 🚀 實現毫秒級音訊播放響應
- 🐛 修復特定日文音訊檔案的多次連線問題
- 🔧 簡化 R2 重試邏輯,提升連線穩定性
- 🛠️ 修復 API URL 尾隨斜線導致的 308 重定向
- 📝 新增 Cloudflare CDN 快取 CORS 問題解決指南
- 🧹 清理生產環境除錯日誌輸出
- 🚀 重大架構升級:客戶端直接存取 Cloudflare R2
- ⚡ 零中轉延遲,消除雙重傳輸問題
- 🔧 智能 CORS 解決方案,避免預檢請求
- 📈 效能提升 90%+,頻寬成本降低 50%
- 🛠️ API 智能回退機制,確保可靠性
- 📚 完整技術文檔和除錯指南
- ✨ 初始版本發布
- 🌍 多語言菜單系統
- 🔊 Azure TTS 整合
- ⚡ 多層級快取架構
- 🎨 字體子集化優化
- 📱 響應式設計
- 🔄 PWA 離線支援
歡迎提交 Issue 和 Pull Request!
- Fork 專案
- 建立功能分支:
git checkout -b feature/amazing-feature - 提交變更:
git commit -m 'Add amazing feature' - 推送到分支:
git push origin feature/amazing-feature - 提交 Pull Request
- 使用 TypeScript 嚴格模式
- 遵循 ESLint 配置
- 組件使用 PascalCase
- 檔案使用 camelCase
此專案採用 MIT 授權條款 - 詳見 LICENSE 檔案。
- Next.js - React 框架
- Azure Cognitive Services - TTS 服務
- Vercel - 部署平台
- Cloudflare - CDN 服務
- Tailwind CSS - CSS 框架
- Shadcn/UI - UI 組件庫
專案維護者: Yelban
最後更新: 2025-07-21
專案狀態: 生產就緒 🚀