iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
自我挑戰組

Google App Script雲端自動化與動態網頁實戰系列 第 20

D20 會議室借用與查詢系統

  • 分享至 

  • xImage
  •  

我們要來介紹前端的部分,我們可以用GAS一次寫在同一個.gs檔案裡,也可以分兩個檔案做。
在查詢的部分,我們可以用後端傳資料給前端,讓前端生成html表格;也可以用後端先生成html表格傳給前端並貼上。這次我們選用後者。

程式碼解析

<body>
        <div class="container">
          <div class="header">
            <div class="logo">📅</div>
            <div>
              <h1>會議室借用系統</h1>
              <div class="sub">快速預約、避免重複、即時查詢</div>
            </div>
          </div>
          <div class="grid">
            <section class="card">
              <h2>借用會議室</h2>
              <div class="hint">填寫資料後按「借用」,系統會自動檢查是否與他人衝突。</div>
              <div class="field">
                <label class="label" for="room">會議室名稱</label>
                <input class="input" id="room" type="text" placeholder="例如:A101 / B201" />
              </div>
              <div class="field">
                <label class="label" for="user">借用人</label>
                <input class="input" id="user" type="text" placeholder="輸入您的姓名" />
              </div>
              <div class="row">
                <div class="field">
                  <label class="label" for="start">開始時間</label>
                  <input class="input" id="start" type="datetime-local" />
                </div>
                <div class="field">
                  <label class="label" for="end">結束時間</label>
                  <input class="input" id="end" type="datetime-local" />
                </div>
              </div>
              <div class="actions">
                <button id="bookBtn" class="btn btn-primary" onclick="book()">
                  立即借用
                </button>
                <button class="btn btn-secondary" onclick="resetForm()">清空</button>
              </div>
              <div id="result" class="msg" style="display:none;"></div>
            </section>
            <section class="card">
              <h2>查詢會議室紀錄</h2>
              <div class="hint">輸入會議室名稱,顯示所有已登記的時段與狀態。</div>
              <div class="toolbar">
                <input class="input" id="findRoom" type="text" placeholder="例如:A101" style="flex:1" />
                <button class="btn btn-secondary" onclick="search()">查詢</button>
              </div>
              <div id="records" class="table-wrap" style="display:none;"></div>
              <div id="empty" class="empty" style="display:none;">尚無資料,或請先輸入會議室名稱查詢。</div>
            </section>
          </div>
        </div>
        <script>
          // 前端 JavaScript 函式
          function showMsg(text, type){
            const el = document.getElementById("result");
            el.style.display = "block";
            el.textContent = text;
            el.className = "msg " + (type === "ok" ? "ok" : type === "err" ? "err" : "");
          }
          function setBookLoading(loading){
            const btn = document.getElementById("bookBtn");
            if(loading){
              btn.disabled = true;
              btn.textContent = "處理中…";
            }else{
              btn.disabled = false;
              btn.textContent = "立即借用";
            }
          }
          function resetForm(){
            document.getElementById("room").value = "";
            document.getElementById("user").value = "";
            document.getElementById("start").value = "";
            document.getElementById("end").value = "";
            document.getElementById("result").style.display = "none";
          }
          function formatDate(v){
            if(!v) return "";
            const d = new Date(v);
            if (isNaN(d.getTime())) return v;
            const pad = n=>String(n).padStart(2,"0");
            return \`\${d.getFullYear()}/\${pad(d.getMonth()+1)}/\${pad(d.getDate())} \${pad(d.getHours())}:\${pad(d.getMinutes())}\`;
          }
          function book() {
            const room = document.getElementById("room").value.trim();
            const user = document.getElementById("user").value.trim();
            const start = document.getElementById("start").value;
            const end = document.getElementById("end").value;
            if (!room || !user || !start || !end) {
              showMsg("⚠️ 請填寫完整資料!", "err");
              return;
            }
            const startMs = new Date(start.replace("T", " ") + ":00").getTime();
            const endMs = new Date(end.replace("T", " ") + ":00").getTime();
            if (startMs >= endMs) {
              showMsg("⚠️ 開始時間必須早於結束時間!", "err");
              return;
            }
            setBookLoading(true);
            google.script.run.withSuccessHandler(msg => {
              const ok = msg.startsWith("✅");
              showMsg(msg, ok ? "ok" : "err");
              if (ok) {
                resetForm();
                showSuccessToast("借用成功 🎉");
              }
              setBookLoading(false);
            }).withFailureHandler(err => {
              showMsg("❌ 送出失敗:" + (err && err.message ? err.message : "未知錯誤"), "err");
              setBookLoading(false);
            }).bookRoom(room, user, startMs, endMs);
          }
          function search() {
  const empty = document.getElementById("empty");
  const records = document.getElementById("records");
  const room = document.getElementById("findRoom").value.trim();

  records.style.display = "none";
  empty.style.display = "none";
  records.innerHTML = "";

  if (!room) {
    empty.textContent = "請輸入會議室名稱後查詢。";
    empty.style.display = "block";
    return;
  }

  google.script.run.withSuccessHandler(html => {
    if (html === '<p class="empty">查無資料。</p>') {
      empty.textContent = "查無資料。";
      empty.style.display = "block";
    } else {
      records.innerHTML = html;
      records.style.display = "block";
    }
  }).withFailureHandler(err => {
    empty.textContent = "查詢失敗:" + (err && err.message ? err.message : "未知錯誤");
    empty.style.display = "block";
  }).getBookings(room);
}
          // 借用成功 toast
          function showSuccessToast(text){
            const toast = document.createElement("div");
            toast.textContent = text;
            toast.style.position = "fixed";
            toast.style.bottom = "30px";
            toast.style.right = "30px";
            toast.style.background = "#10b981";
            toast.style.color = "#fff";
            toast.style.padding = "12px 18px";
            toast.style.borderRadius = "12px";
            toast.style.boxShadow = "0 6px 20px rgba(0,0,0,0.15)";
            toast.style.fontSize = "14px";
            toast.style.zIndex = "9999";
            toast.style.opacity = "0";
            toast.style.transition = "opacity 0.3s ease";
            document.body.appendChild(toast);
            setTimeout(()=>toast.style.opacity="1",50);
            setTimeout(()=>{
              toast.style.opacity="0";
              setTimeout(()=>toast.remove(),300);
            },2500);
          }
        </script>
      </body>

這邊有兩個區塊,一個是登記的地方、另一個是查尋的地方。
showMsg(text, type):一個通用的工具函式,用於在網頁上顯示提示訊息,例如「借用成功」或「請填寫完整資料」。它會動態修改訊息框的文字內容、顯示狀態和樣式。

setBookLoading(loading):用於控制「立即借用」按鈕的狀態。當請求發送時,它會將按鈕設定為「處理中…」並禁用按鈕,防止重複點擊。請求完成後則恢復按鈕狀態。

resetForm():用於清空「借用會議室」表單中的所有輸入欄位,方便使用者重新填寫。

formatDate(v):用於將日期資料格式化為 年/月/日 時:分 的字串格式,方便在網頁表格中顯示。

book():處理使用者點擊「立即借用」按鈕的邏輯。它會先驗證所有輸入欄位是否填寫完整,並檢查結束時間是否晚於開始時間。驗證通過後,它會呼叫後端(Google Apps Script)的 bookRoom 函式來處理借用請求。

search():處理使用者點擊「查詢」按鈕的邏輯。它會從輸入框中獲取會議室名稱,然後呼叫後端(Google Apps Script)的 getBookings 函式來查詢相關紀錄。這個函式還會根據後端回傳的 HTML 內容來動態更新頁面上的表格或顯示「查無資料」的訊息。

showSuccessToast(text):一個用於顯示短暫提示訊息(toast)的工具函式,用於在借用成功時提供一個友善的視覺回饋。它會動態創建一個小彈窗並在幾秒後自動消失。

最後成品

https://ithelp.ithome.com.tw/upload/images/20250828/20169466MQB7rKoT5x.png
輸入資料後,按下立即借用就會存記錄在google試算表
https://ithelp.ithome.com.tw/upload/images/20250828/20169466UaB8zSxviI.png
接著查詢該會議室:a123
https://ithelp.ithome.com.tw/upload/images/20250828/20169466ruyYpRWNhI.png


上一篇
D19 會議室借用與查詢系統
系列文
Google App Script雲端自動化與動態網頁實戰20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言