訂餐系統網頁提示詞:
製作一個訂餐系統網頁,手繪插畫風格,可以使用手機網頁訂餐,姓名、品名、價格、數量,總數、總金額、各別品項加總和總計,資料要串接google試算表(姓名、品名、價格、數量)

第一步:設定 Google 試算表 (資料庫)
- 打開一個新的 Google 試算表。
- 將工作表命名為
orders(或保留預設,但在程式碼中要對應)。 - 在第一列設定標題:
- A1: 時間
- B1: 姓名
- C1: 品名
- D1: 單價
- E1: 數量
- F1: 小計
- 點選上方選單 「擴充功能」 -> 「Apps Script」。






第二步:設定 Google Apps Script (接收端)
將 Apps Script 編輯器中的程式碼清空,貼上以下代碼:
function doPost(e) { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('工作表1'); // 請確認你的工作表名稱 // 解析傳來的 JSON 資料 var data = JSON.parse(e.postData.contents); var name = data.name; var cart = data.cart; // 這是一個包含所有訂購項目的陣列 var timestamp = new Date(); // 迴圈:將購物車內的每一項商品都寫入一行 cart.forEach(function(item) { if(item.qty > 0) { sheet.appendRow([ timestamp, name, item.productName, item.price, item.qty, item.price * item.qty // 小計 ]); } }); // 回傳成功訊息 return ContentService.createTextOutput(JSON.stringify({ 'result': 'success' })) .setMimeType(ContentService.MimeType.JSON); }
部署步驟 (非常重要):
- 點擊右上角 「部署」 -> 「新增部署作業」。
- 類型選擇 「網頁應用程式」。
- 說明隨意填 (例如:訂餐API)。
- 執行身分:選擇 「我」 (你的 Google 帳號)。
- 誰可以存取:選擇 「任何人」 (這點最重要,這樣網頁才能寫入資料)。
- 點擊「部署」,複製產生的 「網頁應用程式網址 (Web App URL)」。
第三步:製作手繪風格網頁 (前端)
你可以將以下程式碼存成一個 .html 檔案 (例如 index.html),或者直接貼到 Codepen 等線上編輯器。
前端使用google協作平台:https://sites.google.com/u/0/new/?authuser=0
請注意: 程式碼最下方的 YOUR_GOOGLE_SCRIPT_URL 必須換成你剛剛複製的網址。
<!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>好食・手繪訂餐</title> <link href="https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@400;700&display=swap" rel="stylesheet"> <style> :root { --paper-color: #fffdf5; --line-color: #2c3e50; --highlight-color: #ffecd1; --accent-color: #ff8b60; } body { font-family: 'Zen Maru Gothic', sans-serif; background-color: #f0f0f0; margin: 0; padding: 20px; color: var(--line-color); background-image: radial-gradient(#ddd 1px, transparent 1px); background-size: 20px 20px; } .container { max-width: 600px; margin: 0 auto; background: var(--paper-color); padding: 25px; /* 手繪風格的不規則邊框 */ border: 3px solid var(--line-color); border-radius: 2% 6% 5% 4% / 1% 1% 2% 4%; box-shadow: 5px 5px 0px rgba(0,0,0,0.1); position: relative; } /* 像膠帶一樣的裝飾 */ .tape { background-color: rgba(255, 255, 255, 0.4); box-shadow: 0 1px 3px rgba(0,0,0,0.2); width: 120px; height: 35px; position: absolute; top: -15px; left: 50%; transform: translateX(-50%) rotate(-2deg); border-left: 1px dashed rgba(0,0,0,0.1); border-right: 1px dashed rgba(0,0,0,0.1); } h1 { text-align: center; font-weight: 700; margin-bottom: 30px; border-bottom: 2px dashed var(--line-color); padding-bottom: 10px; } .form-group { margin-bottom: 20px; } label { display: block; font-weight: bold; margin-bottom: 8px; font-size: 1.1em; } input[type="text"] { width: 100%; padding: 10px; border: 2px solid var(--line-color); border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px; font-family: inherit; font-size: 1rem; background: transparent; box-sizing: border-box; outline: none; } input[type="text"]:focus { background: var(--highlight-color); } /* 菜單列表 */ .menu-item { display: flex; align-items: center; justify-content: space-between; padding: 15px 0; border-bottom: 1px solid #ddd; } .item-info { flex: 2; } .item-name { font-size: 1.1rem; font-weight: bold; } .item-price { color: #666; font-size: 0.9rem; } .item-controls { flex: 1; display: flex; align-items: center; justify-content: flex-end; gap: 10px; } input[type="number"] { width: 50px; padding: 5px; text-align: center; border: 2px solid var(--line-color); border-radius: 10px; font-family: inherit; font-size: 1rem; } .item-total { min-width: 60px; text-align: right; font-weight: bold; } /* 總計區塊 */ .summary { margin-top: 30px; background: var(--highlight-color); padding: 20px; border-radius: 15px; border: 2px dashed var(--line-color); } .summary p { display: flex; justify-content: space-between; margin: 5px 0; font-size: 1.1rem; } .total-price { font-size: 1.5rem !important; font-weight: bold; color: #d35400; border-top: 2px solid var(--line-color); padding-top: 10px; margin-top: 10px !important; } button { width: 100%; padding: 15px; margin-top: 20px; background: var(--accent-color); color: white; border: 3px solid var(--line-color); border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px; font-family: inherit; font-size: 1.2rem; font-weight: bold; cursor: pointer; transition: transform 0.1s; } button:active { transform: scale(0.98); } button:disabled { background: #ccc; cursor: not-allowed; } /* 載入動畫 */ #loading { display: none; text-align: center; margin-top: 10px; } </style> </head> <body> <div class="container"> <div class="tape"></div> <h1>📋 今天吃什麼?</h1> <div class="form-group"> <label for="username">您的姓名 / 稱呼:</label> <input type="text" id="username" placeholder="例如:Mumu 媽咪"> </div> <div id="menu-list"> </div> <div class="summary"> <p>總數量:<span id="total-qty">0</span> 份</p> <p class="total-price">總金額:$<span id="total-amount">0</span></p> </div> <button id="submit-btn" onclick="submitOrder()">送出訂單 ✨</button> <p id="loading">正在手寫紀錄中...</p> </div> <script> // 1. 設定菜單資料 (可在此自由新增修改) const menuData = [ { id: 1, name: "手作日式飯糰", price: 45 }, { id: 2, name: "味噌湯", price: 30 }, { id: 3, name: "炸蝦天婦羅", price: 80 }, { id: 4, name: "抹茶拿鐵", price: 65 } ]; // 2. 初始化網頁 const menuList = document.getElementById('menu-list'); function renderMenu() { menuData.forEach(item => { const div = document.createElement('div'); div.className = 'menu-item'; div.innerHTML = ` <div class="item-info"> <div class="item-name">${item.name}</div> <div class="item-price">$${item.price}</div> </div> <div class="item-controls"> <input type="number" min="0" value="0" data-price="${item.price}" data-name="${item.name}" onchange="calculateTotal()" oninput="calculateTotal()"> <div class="item-total">$0</div> </div> `; menuList.appendChild(div); }); } // 3. 計算總金額與個別小計 function calculateTotal() { let totalQty = 0; let totalAmount = 0; const inputs = document.querySelectorAll('input[type="number"]'); inputs.forEach(input => { const qty = parseInt(input.value) || 0; const price = parseInt(input.dataset.price); const subtotal = qty * price; // 更新該項目的小計顯示 input.nextElementSibling.innerText = '$' + subtotal; // 累加總數 totalQty += qty; totalAmount += subtotal; }); document.getElementById('total-qty').innerText = totalQty; document.getElementById('total-amount').innerText = totalAmount; } // 4. 送出資料到 Google Sheet function submitOrder() { const name = document.getElementById('username').value; const inputs = document.querySelectorAll('input[type="number"]'); // 驗證 if (!name) { alert('請填寫姓名喔!(🎨)'); return; } let cart = []; let hasItem = false; inputs.forEach(input => { const qty = parseInt(input.value) || 0; if (qty > 0) { hasItem = true; cart.push({ productName: input.dataset.name, price: parseInt(input.dataset.price), qty: qty }); } }); if (!hasItem) { alert('購物車是空的呢!請選擇餐點 (🍙)'); return; } // UI 狀態切換 const btn = document.getElementById('submit-btn'); const loading = document.getElementById('loading'); btn.disabled = true; btn.innerText = "傳送中..."; loading.style.display = 'block'; // 準備傳送資料 const data = { name: name, cart: cart }; // *** 請將下方的 URL 替換成你部署後的 Web App URL *** const scriptURL = 'YOUR_GOOGLE_SCRIPT_URL'; fetch(scriptURL, { method: 'POST', body: JSON.stringify(data), // 使用 text/plain 避免 CORS preflight 複雜問題,Google Script 可以處理 headers: { 'Content-Type': 'text/plain;charset=utf-8' } }) .then(response => response.json()) .then(result => { alert('訂單已送出!謝謝光臨 🎉'); // 重置表單 document.getElementById('username').value = ''; inputs.forEach(input => { input.value = 0; input.nextElementSibling.innerText = '$0'; }); calculateTotal(); }) .catch(error => { console.error('Error:', error); alert('發生錯誤,請稍後再試 😢'); }) .finally(() => { btn.disabled = false; btn.innerText = "送出訂單 ✨"; loading.style.display = 'none'; }); } // 啟動渲染 renderMenu(); </script> </body> </html>
專案特色說明:
- 手繪風格設計:
- 使用了 border-radius: 255px 15px... 的 CSS 技巧,讓輸入框和按鈕看起來像手畫的圓圈。
- 使用了 Zen Maru Gothic 圓體字型,讓文字看起來溫潤可愛。
- 背景加上了淡淡的網點紙質感。
- 手機友善 (Responsive):
- 版面會自動適應手機寬度,輸入數字時也很方便。
- 自動計算功能:
- 當你調整數量時,「個別品項加總」、「總數量」和「總金額」會即時變動。
- 聰明的資料紀錄:
- 如果你一次點了「飯糰 x 2」和「味噌湯 x 1」,Google Sheet 會自動新增兩列資料,方便你後續統計每個品項賣了多少。
如何開始使用?
- 完成 Google Sheet 和 Apps Script 的設定。
- 複製 HTML 程式碼。
- 最重要的一步: 找到 HTML 倒數幾行的
const scriptURL = 'YOUR_GOOGLE_SCRIPT_URL';,把你的網址貼進去。 - 用瀏覽器打開 HTML 檔案即可測試!










新增品項提示詞:
新增品名和價格品名價格燒鵝(腿)飯200燒鵝(腿) 湯麵200三寶飯170燒鵝飯170烤鴨飯140玫瑰油雞、白切雞飯140蜜汁叉燒、皮蛋燒肉飯130蜜汁排骨、臘肉飯130臘腸、肝腸飯120油雞腿飯120烤鴨腿飯120叉燒燒肉飯120叉燒油雞飯120燒肉油雞飯120金抄麵200星洲炒米粉120乾炒叉燒河粉170乾炒牛肉河粉170雪菜肉絲(炒/湯) 麵/河/米粉115鹹魚雞粒炒飯140廣州炒飯130牛肉炒飯130臘味炒飯130各式炒飯130蠔油炒牛肉 (大)250蠔油炒牛肉 (小)200豉椒炒回鍋肉 (大)250豉椒炒回鍋肉 (小)200滑蛋炒蝦仁 (大)250滑蛋炒蝦仁 (小)200椒鹽焗蝦球 (大)250椒鹽焗蝦球 (小)200蠔油芥蘭 (大)150蠔油芥蘭 (小)100炒青菜 (大)150炒青菜 (小)100廣州燴飯170三絲飯150滑蛋蝦仁飯130滑蛋牛肉飯120黑胡椒牛肉飯120沙茶牛肉飯120時菜牛肉飯120窩蛋牛肉飯120時菜肉片飯120蒜苔腰肝飯120咖哩腰肝飯120涼薯腰肝飯120透抽腰肝飯120鮮蝦雲吞湯70肉絲時蔬湯60魚片及蛋湯80蛋花肉絲湯60豬肝湯50
請複製下方完整的 index.html 程式碼:
記得將倒數第 20 行左右的 'YOUR_GOOGLE_SCRIPT_URL' 換回您原本的網址喔!
<!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>燒臘食堂・手繪點餐</title> <link href="https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@400;700&display=swap" rel="stylesheet"> <style> :root { --paper-color: #fffdf5; --line-color: #2c3e50; --highlight-color: #ffecd1; --accent-color: #d35400; /* 改成更有食慾的燒臘色 */ } body { font-family: 'Zen Maru Gothic', sans-serif; background-color: #f0f0f0; margin: 0; padding: 20px; color: var(--line-color); background-image: radial-gradient(#ddd 1px, transparent 1px); background-size: 20px 20px; } .container { max-width: 600px; margin: 0 auto; background: var(--paper-color); padding: 25px; border: 3px solid var(--line-color); border-radius: 2% 6% 5% 4% / 1% 1% 2% 4%; box-shadow: 5px 5px 0px rgba(0,0,0,0.1); position: relative; } .tape { background-color: rgba(200, 50, 50, 0.4); /* 紅色膠帶 */ box-shadow: 0 1px 3px rgba(0,0,0,0.2); width: 120px; height: 35px; position: absolute; top: -15px; left: 50%; transform: translateX(-50%) rotate(-2deg); border-left: 1px dashed rgba(0,0,0,0.1); border-right: 1px dashed rgba(0,0,0,0.1); } h1 { text-align: center; font-weight: 700; margin-bottom: 20px; border-bottom: 2px dashed var(--line-color); padding-bottom: 10px; } /* 分類標題樣式 */ .category-title { background: var(--line-color); color: #fff; padding: 5px 15px; border-radius: 20px; display: inline-block; margin-top: 25px; margin-bottom: 10px; font-size: 1rem; transform: rotate(-1deg); } .form-group { margin-bottom: 20px; } label { display: block; font-weight: bold; margin-bottom: 8px; font-size: 1.1em; } input[type="text"] { width: 100%; padding: 10px; border: 2px solid var(--line-color); border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px; font-family: inherit; font-size: 1rem; background: transparent; box-sizing: border-box; outline: none; } input[type="text"]:focus { background: var(--highlight-color); } /* 為了長菜單,加上捲軸 */ #menu-list { max-height: 60vh; /* 限制高度,避免頁面太長 */ overflow-y: auto; padding-right: 5px; border-top: 1px solid #eee; } .menu-item { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px dashed #ccc; } .item-info { flex: 2; } .item-name { font-size: 1.05rem; font-weight: bold; } .item-price { color: #d35400; font-weight: bold; font-size: 0.95rem; } .item-controls { flex: 1; display: flex; align-items: center; justify-content: flex-end; gap: 8px; } input[type="number"] { width: 45px; padding: 5px; text-align: center; border: 2px solid var(--line-color); border-radius: 8px; font-family: inherit; font-size: 1rem; } .item-total { min-width: 50px; text-align: right; font-size: 0.9rem; color: #666; } .summary { margin-top: 20px; background: var(--highlight-color); padding: 20px; border-radius: 15px; border: 2px dashed var(--line-color); } .summary p { display: flex; justify-content: space-between; margin: 5px 0; font-size: 1.1rem; } .total-price { font-size: 1.5rem !important; font-weight: bold; color: #d35400; border-top: 2px solid var(--line-color); padding-top: 10px; margin-top: 10px !important; } button { width: 100%; padding: 15px; margin-top: 20px; background: var(--accent-color); color: white; border: 3px solid var(--line-color); border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px; font-family: inherit; font-size: 1.2rem; font-weight: bold; cursor: pointer; } button:disabled { background: #999; } #loading { display: none; text-align: center; margin-top: 10px; } </style> </head> <body> <div class="container"> <div class="tape"></div> <h1>🥢 港式燒臘點餐</h1> <div class="form-group"> <label for="username">您的姓名 / 桌號:</label> <input type="text" id="username" placeholder="例如:陳小姐 / 3號桌"> </div> <div id="menu-list"> </div> <div class="form-group" style="margin-top: 20px;"> <label for="remark">備註 (口味、不蔥、麵/河粉):</label> <input type="text" id="remark" placeholder="例如:雪菜肉絲要湯米粉..."> </div> <div class="summary"> <p>總數量:<span id="total-qty">0</span> 份</p> <p class="total-price">總金額:$<span id="total-amount">0</span></p> </div> <button id="submit-btn" onclick="submitOrder()">送出訂單 🔥</button> <p id="loading">正在傳送給老闆...</p> </div> <script> // 設定新菜單資料 (已分類) const menuData = [ // 燒臘飯類 { category: "燒臘飯類", items: [ { name: "燒鵝(腿)飯", price: 200 }, { name: "三寶飯", price: 170 }, { name: "燒鵝飯", price: 170 }, { name: "烤鴨飯", price: 140 }, { name: "玫瑰油雞/白切雞飯", price: 140 }, { name: "蜜汁叉燒/皮蛋燒肉飯", price: 130 }, { name: "蜜汁排骨/臘肉飯", price: 130 }, { name: "臘腸/肝腸飯", price: 120 }, { name: "油雞腿飯", price: 120 }, { name: "烤鴨腿飯", price: 120 }, { name: "叉燒燒肉飯", price: 120 }, { name: "叉燒油雞飯", price: 120 }, { name: "燒肉油雞飯", price: 120 }, { name: "廣州燴飯", price: 170 }, { name: "三絲飯", price: 150 }, { name: "滑蛋蝦仁飯", price: 130 }, { name: "滑蛋牛肉飯", price: 120 }, { name: "黑胡椒牛肉飯", price: 120 }, { name: "沙茶牛肉飯", price: 120 }, { name: "時菜牛肉飯", price: 120 }, { name: "窩蛋牛肉飯", price: 120 }, { name: "時菜肉片飯", price: 120 }, { name: "蒜苔腰肝飯", price: 120 }, { name: "咖哩腰肝飯", price: 120 }, { name: "涼薯腰肝飯", price: 120 }, { name: "透抽腰肝飯", price: 120 } ]}, // 麵食類 { category: "粉/麵/粥", items: [ { name: "燒鵝(腿) 湯麵", price: 200 }, { name: "金抄麵", price: 200 }, { name: "星洲炒米粉", price: 120 }, { name: "乾炒叉燒河粉", price: 170 }, { name: "乾炒牛肉河粉", price: 170 }, { name: "雪菜肉絲(炒/湯) 麵/河/米粉", price: 115 } ]}, // 炒飯類 { category: "炒飯類", items: [ { name: "鹹魚雞粒炒飯", price: 140 }, { name: "廣州炒飯", price: 130 }, { name: "牛肉炒飯", price: 130 }, { name: "臘味炒飯", price: 130 }, { name: "各式炒飯", price: 130 } ]}, // 熱炒單點 { category: "熱炒單點", items: [ { name: "蠔油炒牛肉 (大)", price: 250 }, { name: "蠔油炒牛肉 (小)", price: 200 }, { name: "豉椒炒回鍋肉 (大)", price: 250 }, { name: "豉椒炒回鍋肉 (小)", price: 200 }, { name: "滑蛋炒蝦仁 (大)", price: 250 }, { name: "滑蛋炒蝦仁 (小)", price: 200 }, { name: "椒鹽焗蝦球 (大)", price: 250 }, { name: "椒鹽焗蝦球 (小)", price: 200 }, { name: "蠔油芥蘭 (大)", price: 150 }, { name: "蠔油芥蘭 (小)", price: 100 }, { name: "炒青菜 (大)", price: 150 }, { name: "炒青菜 (小)", price: 100 } ]}, // 湯類 { category: "湯類", items: [ { name: "鮮蝦雲吞湯", price: 70 }, { name: "肉絲時蔬湯", price: 60 }, { name: "魚片及蛋湯", price: 80 }, { name: "蛋花肉絲湯", price: 60 }, { name: "豬肝湯", price: 50 } ]} ]; const menuList = document.getElementById('menu-list'); // 渲染菜單 (含分類標題) function renderMenu() { menuData.forEach(group => { // 加入分類標題 const title = document.createElement('div'); title.className = 'category-title'; title.innerText = group.category; menuList.appendChild(title); // 加入該分類下的項目 group.items.forEach(item => { const div = document.createElement('div'); div.className = 'menu-item'; div.innerHTML = ` <div class="item-info"> <div class="item-name">${item.name}</div> <div class="item-price">$${item.price}</div> </div> <div class="item-controls"> <input type="number" min="0" value="0" data-price="${item.price}" data-name="${item.name}" onchange="calculateTotal()" oninput="calculateTotal()" onclick="this.select();"> <div class="item-total">$0</div> </div> `; menuList.appendChild(div); }); }); } function calculateTotal() { let totalQty = 0; let totalAmount = 0; const inputs = document.querySelectorAll('input[type="number"]'); inputs.forEach(input => { const qty = parseInt(input.value) || 0; const price = parseInt(input.dataset.price); const subtotal = qty * price; input.nextElementSibling.innerText = '$' + subtotal; totalQty += qty; totalAmount += subtotal; }); document.getElementById('total-qty').innerText = totalQty; document.getElementById('total-amount').innerText = totalAmount; } function submitOrder() { const name = document.getElementById('username').value; const remark = document.getElementById('remark').value; // 取得備註 const inputs = document.querySelectorAll('input[type="number"]'); if (!name) { alert('請填寫姓名或桌號喔!'); return; } let cart = []; let hasItem = false; inputs.forEach(input => { const qty = parseInt(input.value) || 0; if (qty > 0) { hasItem = true; cart.push({ productName: input.dataset.name, price: parseInt(input.dataset.price), qty: qty }); } }); if (!hasItem) { alert('請至少選擇一項餐點 🥢'); return; } const btn = document.getElementById('submit-btn'); const loading = document.getElementById('loading'); btn.disabled = true; btn.innerText = "傳送中..."; loading.style.display = 'block'; // 將備註加在名字後面,這樣不用改後端 Google Script // 試算表上的「姓名」欄位會顯示:王小明 (備註: 不要蔥) const finalName = remark ? `${name} (備註: ${remark})` : name; const data = { name: finalName, cart: cart }; // *** 這裡請填入您的 Google Script 網址 *** const scriptURL = 'YOUR_GOOGLE_SCRIPT_URL'; fetch(scriptURL, { method: 'POST', body: JSON.stringify(data), headers: { 'Content-Type': 'text/plain;charset=utf-8' } }) .then(response => response.json()) .then(result => { alert('點餐成功!廚房準備中 🔥'); location.reload(); // 成功後重新整理頁面 }) .catch(error => { console.error('Error:', error); alert('連線怪怪的,請通知老闆 😢'); btn.disabled = false; btn.innerText = "送出訂單 🔥"; loading.style.display = 'none'; }); } renderMenu(); </script> </body> </html>
這次的微調重點:
- 資料結構化:我將「玫瑰油雞、白切雞飯」改成「玫瑰油雞/白切雞飯」,讓介面更簡潔。
- 不需更改後端:為了讓您方便,我把「備註」的內容自動合併在「姓名」欄位傳送。
- 例如: 如果客人填寫 姓名:Mumu,備註:河粉要乾炒
- 試算表會顯示: Mumu (備註: 河粉要乾炒)
- 這樣您看單子時就會一目了然,也不用重新部署 Apps Script!
跳出視窗顯示"完成點餐"提示詞
送出後請跳出視窗顯示"完成點餐",有一個按鈕可以關閉視窗,返回原點餐畫面
這個修改包含三個部分:
- CSS:新增彈出視窗的樣式。
- HTML:在頁面中加入視窗的骨架。
- JavaScript:修改送出後的行為,改成顯示視窗並在關閉時重置表單。
以下是修改後的完整程式碼,您可以直接全選複製覆蓋舊檔案(記得再次填入您的 Google Script URL):
<!DOCTYPE html> <html lang="zh-TW"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>燒臘食堂・手繪點餐</title> <link href="https://fonts.googleapis.com/css2?family=Zen+Maru+Gothic:wght@400;700&display=swap" rel="stylesheet"> <style> :root { --paper-color: #fffdf5; --line-color: #2c3e50; --highlight-color: #ffecd1; --accent-color: #d35400; } body { font-family: 'Zen Maru Gothic', sans-serif; background-color: #f0f0f0; margin: 0; padding: 20px; color: var(--line-color); background-image: radial-gradient(#ddd 1px, transparent 1px); background-size: 20px 20px; } .container { max-width: 600px; margin: 0 auto; background: var(--paper-color); padding: 25px; border: 3px solid var(--line-color); border-radius: 2% 6% 5% 4% / 1% 1% 2% 4%; box-shadow: 5px 5px 0px rgba(0,0,0,0.1); position: relative; } .tape { background-color: rgba(200, 50, 50, 0.4); box-shadow: 0 1px 3px rgba(0,0,0,0.2); width: 120px; height: 35px; position: absolute; top: -15px; left: 50%; transform: translateX(-50%) rotate(-2deg); border-left: 1px dashed rgba(0,0,0,0.1); border-right: 1px dashed rgba(0,0,0,0.1); } h1 { text-align: center; font-weight: 700; margin-bottom: 20px; border-bottom: 2px dashed var(--line-color); padding-bottom: 10px; } .category-title { background: var(--line-color); color: #fff; padding: 5px 15px; border-radius: 20px; display: inline-block; margin-top: 25px; margin-bottom: 10px; font-size: 1rem; transform: rotate(-1deg); } .form-group { margin-bottom: 20px; } label { display: block; font-weight: bold; margin-bottom: 8px; font-size: 1.1em; } input[type="text"] { width: 100%; padding: 10px; border: 2px solid var(--line-color); border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px; font-family: inherit; font-size: 1rem; background: transparent; box-sizing: border-box; outline: none; } input[type="text"]:focus { background: var(--highlight-color); } #menu-list { max-height: 60vh; overflow-y: auto; padding-right: 5px; border-top: 1px solid #eee; } .menu-item { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px dashed #ccc; } .item-info { flex: 2; } .item-name { font-size: 1.05rem; font-weight: bold; } .item-price { color: #d35400; font-weight: bold; font-size: 0.95rem; } .item-controls { flex: 1; display: flex; align-items: center; justify-content: flex-end; gap: 8px; } input[type="number"] { width: 45px; padding: 5px; text-align: center; border: 2px solid var(--line-color); border-radius: 8px; font-family: inherit; font-size: 1rem; } .item-total { min-width: 50px; text-align: right; font-size: 0.9rem; color: #666; } .summary { margin-top: 20px; background: var(--highlight-color); padding: 20px; border-radius: 15px; border: 2px dashed var(--line-color); } .summary p { display: flex; justify-content: space-between; margin: 5px 0; font-size: 1.1rem; } .total-price { font-size: 1.5rem !important; font-weight: bold; color: #d35400; border-top: 2px solid var(--line-color); padding-top: 10px; margin-top: 10px !important; } button { width: 100%; padding: 15px; margin-top: 20px; background: var(--accent-color); color: white; border: 3px solid var(--line-color); border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px; font-family: inherit; font-size: 1.2rem; font-weight: bold; cursor: pointer; transition: transform 0.1s; } button:active { transform: scale(0.98); } button:disabled { background: #999; cursor: not-allowed; } #loading { display: none; text-align: center; margin-top: 10px; } /* --- 新增:彈出視窗 (Modal) 的樣式 --- */ .modal-overlay { display: none; /* 預設隱藏 */ position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); /* 半透明黑色背景 */ z-index: 999; justify-content: center; align-items: center; backdrop-filter: blur(2px); /* 背景模糊效果 */ } .modal-box { background: var(--paper-color); width: 80%; max-width: 350px; padding: 30px; text-align: center; border: 3px solid var(--line-color); /* 延續手繪不規則圓角風格 */ border-radius: 2% 6% 5% 4% / 1% 1% 2% 4%; box-shadow: 10px 10px 0px rgba(0,0,0,0.2); position: relative; animation: popIn 0.3s ease-out; } @keyframes popIn { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } } .modal-icon { font-size: 3rem; margin-bottom: 10px; display: block; } .modal-title { font-size: 1.5rem; font-weight: bold; margin-bottom: 10px; color: var(--accent-color); } .modal-desc { margin-bottom: 25px; line-height: 1.5; } .modal-btn { background: var(--line-color); /* 關閉按鈕用深色 */ color: white; margin-top: 0; /* 覆蓋原本 button 的 margin */ } </style> </head> <body> <div class="container"> <div class="tape"></div> <h1>🥢 港式燒臘點餐</h1> <div class="form-group"> <label for="username">您的姓名 / 桌號:</label> <input type="text" id="username" placeholder="例如:陳小姐 / 3號桌"> </div> <div id="menu-list"> </div> <div class="form-group" style="margin-top: 20px;"> <label for="remark">備註 (口味、不蔥、麵/河粉):</label> <input type="text" id="remark" placeholder="例如:雪菜肉絲要湯米粉..."> </div> <div class="summary"> <p>總數量:<span id="total-qty">0</span> 份</p> <p class="total-price">總金額:$<span id="total-amount">0</span></p> </div> <button id="submit-btn" onclick="submitOrder()">送出訂單 🔥</button> <p id="loading">正在傳送給老闆...</p> </div> <div id="success-modal" class="modal-overlay"> <div class="modal-box"> <span class="modal-icon">🎉</span> <div class="modal-title">完成點餐</div> <div class="modal-desc"> 您的訂單已送達廚房!<br> 稍後為您送上美味餐點。 </div> <button class="modal-btn" onclick="closeModal()">關閉視窗</button> </div> </div> <script> const menuData = [ { category: "燒臘飯類", items: [ { name: "燒鵝(腿)飯", price: 200 }, { name: "三寶飯", price: 170 }, { name: "燒鵝飯", price: 170 }, { name: "烤鴨飯", price: 140 }, { name: "玫瑰油雞/白切雞飯", price: 140 }, { name: "蜜汁叉燒/皮蛋燒肉飯", price: 130 }, { name: "蜜汁排骨/臘肉飯", price: 130 }, { name: "臘腸/肝腸飯", price: 120 }, { name: "油雞腿飯", price: 120 }, { name: "烤鴨腿飯", price: 120 }, { name: "叉燒燒肉飯", price: 120 }, { name: "叉燒油雞飯", price: 120 }, { name: "燒肉油雞飯", price: 120 }, { name: "廣州燴飯", price: 170 }, { name: "三絲飯", price: 150 }, { name: "滑蛋蝦仁飯", price: 130 }, { name: "滑蛋牛肉飯", price: 120 }, { name: "黑胡椒牛肉飯", price: 120 }, { name: "沙茶牛肉飯", price: 120 }, { name: "時菜牛肉飯", price: 120 }, { name: "窩蛋牛肉飯", price: 120 }, { name: "時菜肉片飯", price: 120 }, { name: "蒜苔腰肝飯", price: 120 }, { name: "咖哩腰肝飯", price: 120 }, { name: "涼薯腰肝飯", price: 120 }, { name: "透抽腰肝飯", price: 120 } ]}, { category: "粉/麵/粥", items: [ { name: "燒鵝(腿) 湯麵", price: 200 }, { name: "金抄麵", price: 200 }, { name: "星洲炒米粉", price: 120 }, { name: "乾炒叉燒河粉", price: 170 }, { name: "乾炒牛肉河粉", price: 170 }, { name: "雪菜肉絲(炒/湯) 麵/河/米粉", price: 115 } ]}, { category: "炒飯類", items: [ { name: "鹹魚雞粒炒飯", price: 140 }, { name: "廣州炒飯", price: 130 }, { name: "牛肉炒飯", price: 130 }, { name: "臘味炒飯", price: 130 }, { name: "各式炒飯", price: 130 } ]}, { category: "熱炒單點", items: [ { name: "蠔油炒牛肉 (大)", price: 250 }, { name: "蠔油炒牛肉 (小)", price: 200 }, { name: "豉椒炒回鍋肉 (大)", price: 250 }, { name: "豉椒炒回鍋肉 (小)", price: 200 }, { name: "滑蛋炒蝦仁 (大)", price: 250 }, { name: "滑蛋炒蝦仁 (小)", price: 200 }, { name: "椒鹽焗蝦球 (大)", price: 250 }, { name: "椒鹽焗蝦球 (小)", price: 200 }, { name: "蠔油芥蘭 (大)", price: 150 }, { name: "蠔油芥蘭 (小)", price: 100 }, { name: "炒青菜 (大)", price: 150 }, { name: "炒青菜 (小)", price: 100 } ]}, { category: "湯類", items: [ { name: "鮮蝦雲吞湯", price: 70 }, { name: "肉絲時蔬湯", price: 60 }, { name: "魚片及蛋湯", price: 80 }, { name: "蛋花肉絲湯", price: 60 }, { name: "豬肝湯", price: 50 } ]} ]; const menuList = document.getElementById('menu-list'); function renderMenu() { menuData.forEach(group => { const title = document.createElement('div'); title.className = 'category-title'; title.innerText = group.category; menuList.appendChild(title); group.items.forEach(item => { const div = document.createElement('div'); div.className = 'menu-item'; div.innerHTML = ` <div class="item-info"> <div class="item-name">${item.name}</div> <div class="item-price">$${item.price}</div> </div> <div class="item-controls"> <input type="number" min="0" value="0" data-price="${item.price}" data-name="${item.name}" onchange="calculateTotal()" oninput="calculateTotal()" onclick="this.select();"> <div class="item-total">$0</div> </div> `; menuList.appendChild(div); }); }); } function calculateTotal() { let totalQty = 0; let totalAmount = 0; const inputs = document.querySelectorAll('input[type="number"]'); inputs.forEach(input => { const qty = parseInt(input.value) || 0; const price = parseInt(input.dataset.price); const subtotal = qty * price; input.nextElementSibling.innerText = '$' + subtotal; totalQty += qty; totalAmount += subtotal; }); document.getElementById('total-qty').innerText = totalQty; document.getElementById('total-amount').innerText = totalAmount; } function submitOrder() { const name = document.getElementById('username').value; const remark = document.getElementById('remark').value; const inputs = document.querySelectorAll('input[type="number"]'); if (!name) { alert('請填寫姓名或桌號喔!'); return; } let cart = []; let hasItem = false; inputs.forEach(input => { const qty = parseInt(input.value) || 0; if (qty > 0) { hasItem = true; cart.push({ productName: input.dataset.name, price: parseInt(input.dataset.price), qty: qty }); } }); if (!hasItem) { alert('請至少選擇一項餐點 🥢'); return; } const btn = document.getElementById('submit-btn'); const loading = document.getElementById('loading'); btn.disabled = true; btn.innerText = "傳送中..."; loading.style.display = 'block'; const finalName = remark ? `${name} (備註: ${remark})` : name; const data = { name: finalName, cart: cart }; // *** 這裡請填入您的 Google Script 網址 *** const scriptURL = 'YOUR_GOOGLE_SCRIPT_URL'; fetch(scriptURL, { method: 'POST', body: JSON.stringify(data), headers: { 'Content-Type': 'text/plain;charset=utf-8' } }) .then(response => response.json()) .then(result => { // 成功後,不重整頁面,改為顯示彈出視窗 document.getElementById('success-modal').style.display = 'flex'; loading.style.display = 'none'; }) .catch(error => { console.error('Error:', error); alert('連線怪怪的,請通知老闆 😢'); btn.disabled = false; btn.innerText = "送出訂單 🔥"; loading.style.display = 'none'; }); } // 新增:關閉視窗並清空表單 function closeModal() { // 1. 隱藏視窗 document.getElementById('success-modal').style.display = 'none'; // 2. 清空輸入欄位 document.getElementById('username').value = ''; document.getElementById('remark').value = ''; // 3. 歸零所有數量 const inputs = document.querySelectorAll('input[type="number"]'); inputs.forEach(input => { input.value = 0; input.nextElementSibling.innerText = '$0'; }); // 4. 重新計算總金額 (歸零) calculateTotal(); // 5. 恢復送出按鈕狀態 const btn = document.getElementById('submit-btn'); btn.disabled = false; btn.innerText = "送出訂單 🔥"; // 6. 捲動回頂部 window.scrollTo({ top: 0, behavior: 'smooth' }); } renderMenu(); </script> </body> </html>
修改亮點:
- 手繪風視窗:視窗樣式(
.modal-box)使用了與主畫面相同的border-radius技巧和紙張背景色,保持視覺一致性。 - 不重整頁面:當按下「關閉視窗」時,我寫了一個
closeModal()函式,它會手動把所有數字歸零、清空名字,讓使用者可以馬上幫下一位點餐,體驗更流暢(就像原生 APP 一樣)。 - 視覺回饋:加入了一個簡單的
popIn動畫,讓視窗跳出來時有彈性效果。
點餐網頁:
https://sites.google.com/view/mumufood/%E9%BB%9E%E9%A4%90%E7%B6%B2%E9%A0%81
GeminiAI問答網頁:
https://gemini.google.com/share/e3f6d0943b21
























