0. 作品介紹

作者 新竹教師測試員 教育處

方便辦公室同仁訂飲料跟統計金額


108 1 0
非教學
封面圖 封面

1. AI 提示詞

把這段指令貼給 AI,讓它幫你生成程式碼。

我想要建立一個訂飲料表
幫我用Google 試算表建立 【飲料與點餐名單資料庫】
我希望有些資料我可以直接在試算表更改後,首頁可以直接呈現,並且送單之後會自動更新頁面

需求說明 【
1.請列出各種種口味的飲料與價格
2.建立10位公司成員的名單
3.統計各種口味飲料的數量
4.統計金錢總額


要把它變成程式碼我要發佈
請完整敘述操作步驟 越詳細越好
作業環境
Google試算表
Google Apps Script

請給我後端gs完整程式碼、前端index完整程式碼

2. 資料表規則

請照下面規則建立分頁與欄位。

第1分頁: 菜單 A 欄: 品項 B 欄: 價格 第2分頁: 名單 A 欄: 姓名 第3分頁: 訂單紀錄 A 欄: 時間戳記 B 欄: 姓名 C 欄: 飲品 D 欄: 價格

📄 解析結果(自動表格)

分頁:菜單
A 欄 B 欄
品項 價格
分頁:名單
A 欄
姓名
分頁:訂單紀錄
A 欄 B 欄 C 欄 D 欄
時間戳記 姓名 飲品 價格

3. 開啟 Apps Script

從試算表開 Apps Script,才能直接用 SpreadsheetApp 讀寫資料。

  1. 試算表上方選單:擴充功能 → Apps Script
  2. 刪掉預設的程式碼(或全部選取覆蓋)
  3. 保留/建立檔案:
    • Code.gs
    • index.html

4. 貼上後端(Code.gs)

把後端程式貼到 Apps Script 的 Code.gs。

Code.gs(後端)
/**
 * 初始化網頁
 */
function doGet() {
  return HtmlService.createTemplateFromFile('index')
    .evaluate()
    .setTitle('辦公室訂飲料系統')
    .addMetaTag('viewport', 'width=device-width, initial-scale=1');
}

/**
 * 讀取試算表資料(選單、名單、統計)
 */
function getData() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  
  // 1. 取得菜單與名單
  const menuData = ss.getSheetByName("菜單").getDataRange().getValues();
  const memberData = ss.getSheetByName("名單").getDataRange().getValues();
  const orderData = ss.getSheetByName("訂單紀錄").getDataRange().getValues();

  // 移除標題列
  menuData.shift();
  memberData.shift();
  orderData.shift();

  // 2. 統計邏輯
  let stats = {};
  let totalCash = 0;
  
  orderData.forEach(row => {
    const drink = row[2];
    const price = Number(row[3]);
    stats[drink] = (stats[drink] || 0) + 1;
    totalCash += price;
  });

  return {
    menu: menuData,
    members: memberData,
    stats: stats,
    totalCash: totalCash
  };
}

/**
 * 提交訂單
 */
function submitOrder(formData) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName("訂單紀錄");
  
  sheet.appendRow([
    new Date(), 
    formData.name, 
    formData.drink, 
    formData.price
  ]);
  
  return "提交成功!";
}
貼到 Apps Script 的 Code.gs 檔案

5. 貼上前端(index.html)

把前端程式貼到 Apps Script 的 index.html。

index.html(前端)
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
  <style>
    body { padding: 20px; background-color: #f0f2f5; font-family: "Microsoft JhengHei", sans-serif; }
    .card { border: none; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); margin-bottom: 20px; }
    .card-title { color: #0d6efd; font-weight: bold; }
    .status-box { background: #fff; padding: 15px; border-radius: 8px; }
    #loadingOverlay {
      position: fixed; top: 0; left: 0; width: 100%; height: 100%;
      background: rgba(255,255,255,0.7); display: none; justify-content: center;
      align-items: center; z-index: 1000;
    }
  </style>
</head>
<body>

  <div id="loadingOverlay">
    <div class="spinner-border text-primary" role="status"></div>
  </div>

  <div class="container">
    <h2 class="text-center mb-4">🧋 辦公室訂飲系統 (自動更新版)</h2>

    <div class="row">
      <div class="col-md-5">
        <div class="card p-4">
          <h5 class="card-title">我要訂餐</h5>
          <hr>
          <form id="orderForm">
            <div class="mb-3">
              <label class="form-label">你的名字</label>
              <select class="form-select" id="name" required>
                <option value="" disabled selected>請選擇姓名</option>
              </select>
            </div>
            <div class="mb-3">
              <label class="form-label">選擇飲品</label>
              <select class="form-select" id="drink" onchange="updatePrice()" required>
                <option value="" disabled selected>請選擇飲料</option>
              </select>
            </div>
            <div class="mb-3">
              <label class="form-label">應付金額:<span id="priceLabel" class="fs-4 text-danger">0</span> 元</label>
              <input type="hidden" id="priceInput">
            </div>
            <button type="submit" id="submitBtn" class="btn btn-primary w-100 py-2">確認提交訂單</button>
          </form>
        </div>
      </div>

      <div class="col-md-7">
        <div class="card p-4">
          <h5 class="card-title">即時訂單統計</h5>
          <hr>
          <div id="statsContent">
             <div class="text-center">讀取資料中...</div>
          </div>
          <div class="mt-4 pt-3 border-top">
            <h4 class="d-flex justify-content-between">
              <span>總計金額</span>
              <span class="text-success">$<span id="totalCash">0</span></span>
            </h4>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script>
    let globalMenu = [];

    // 頁面開啟時自動執行
    window.onload = function() {
      refreshData();
    };

    // 核心功能:從試算表抓取最新資料並更新畫面
    function refreshData() {
      showLoading(true);
      google.script.run.withSuccessHandler(function(data) {
        // 1. 更新姓名選單 (僅在第一次載入時或手動重新整理時)
        const nameSelect = document.getElementById('name');
        if (nameSelect.options.length <= 1) { 
            data.members.forEach(m => {
              nameSelect.innerHTML += `<option value="${m[0]}">${m[0]}</option>`;
            });
        }

        // 2. 更新飲品菜單
        globalMenu = data.menu;
        const drinkSelect = document.getElementById('drink');
        const currentDrink = drinkSelect.value; // 保留當前選擇
        drinkSelect.innerHTML = '<option value="" disabled>請選擇飲料</option>';
        data.menu.forEach((d, index) => {
          drinkSelect.innerHTML += `<option value="${d[0]}" data-index="${index}">${d[0]} ($${d[1]})</option>`;
        });
        drinkSelect.value = currentDrink;

        // 3. 更新統計列表 (需求 3)
        renderStats(data.stats);

        // 4. 更新總金額 (需求 4)
        document.getElementById('totalCash').innerText = data.totalCash;
        
        showLoading(false);
      }).getData();
    }

    // 渲染統計清單
    function renderStats(stats) {
      let html = '<table class="table table-hover"><thead><tr><th>飲品名稱</th><th class="text-end">數量</th></tr></thead><tbody>';
      let hasData = false;
      for (let drink in stats) {
        hasData = true;
        html += `<tr>
                  <td>${drink}</td>
                  <td class="text-end"><span class="badge bg-info text-dark">${stats[drink]} 杯</span></td>
                </tr>`;
      }
      html += '</tbody></table>';
      document.getElementById('statsContent').innerHTML = hasData ? html : '<div class="text-muted text-center">目前還沒有人訂購喔!</div>';
    }

    // 更新單價顯示
    function updatePrice() {
      const select = document.getElementById('drink');
      const index = select.options[select.selectedIndex].getAttribute('data-index');
      if (index !== null) {
        const price = globalMenu[index][1];
        document.getElementById('priceLabel').innerText = price;
        document.getElementById('priceInput').value = price;
      }
    }

    // 處理表單提交
    document.getElementById('orderForm').onsubmit = function(e) {
      e.preventDefault();
      const btn = document.getElementById('submitBtn');
      
      btn.disabled = true;
      btn.innerText = '傳送中...';

      const formData = {
        name: document.getElementById('name').value,
        drink: document.getElementById('drink').value,
        price: document.getElementById('priceInput').value
      };

      // 呼叫後端 gs
      google.script.run.withSuccessHandler(function(msg) {
        alert("🎉 " + msg);
        
        // 重置表單
        document.getElementById('orderForm').reset();
        document.getElementById('priceLabel').innerText = '0';
        btn.disabled = false;
        btn.innerText = '確認提交訂單';

        // 重點:提交成功後,立即重新抓取資料更新統計區塊,而不重新整理整個網頁
        refreshData();
      }).submitOrder(formData);
    };

    function showLoading(show) {
      document.getElementById('loadingOverlay').style.display = show ? 'flex' : 'none';
    }
  </script>
</body>
</html>
貼到 Apps Script 的 index.html 檔案

6. 部署

部署成 Web App 後,就會拿到可分享網址。

  1. Apps Script 右上:部署 → 新部署
  2. 類型選:Web app
  3. 執行身分:你
  4. 存取權限:任何人
  5. 完成後複製網址

💬 評論區

老師們的交流與提問

載入評論中...