Skip to content

竹苗週網站建置說明書

簡介

本文件是竹苗週網站建置說明書,主要是為了讓竹苗週的工作人員能夠快速上手,並且能夠獨立維護網站。

網站架構

網站目錄結構

前端配置

後端配置

網站後端使用 Google Sheets 作為資料庫,並以 Google Apps Script 作為後端程式,提供網站所需的 API。

資料庫由兩個工作表組成,分別負責商品資料及資料收集。
以下提及這兩份工作表將以 商品資料資料收集 代稱,如出現在程式碼中,請自行代換為實際工作表名稱。

Google Sheets 資料庫

商品資料

名稱備註最小價格最大價格原價圖片連結詳細說明商品分類選項1選項2...
  • 名稱: 商品名稱,必填,不可重複
  • 備註: 商品簡介,選填,顯示於商品名稱下方,支持 HTML 標籤
  • 最小價格: 商品最低價格,必填
  • 最大價格: 商品最高價格,選填
  • 原價: 商品原價,選填
  • 圖片連結: 商品圖片連結,必填
  • 詳細說明: 商品詳細說明,選填,支持 HTML 標籤
  • 商品分類: 商品分類,選填,支持多個分類,以換行分隔
  • 選項: 商品選項,選填,每個選項一欄,格式如下:
    • 第一行: 選項名稱
    • 第二行: 選項價格
    • 第三行: 選項標籤

資料收集

時間姓名電話商品名稱商品選項數量備註...

Google Apps Script 後端程式

商品資料 API

javascript
function doGet(e) {
  // var params = {};         // 如果有 GET 參數判斷用
  // if (e) {                 // 不使用可刪除或註解
  //   params = e.parameter;
  // }

  var spreadSheet = SpreadsheetApp.openById("試算表ID"); // 請自行替換為實際試算表 ID
  var sheet = spreadSheet.getSheetByName("商品資料"); // 請自行替換為實際工作表名稱
  var data = sheet.getDataRange().getValues();

  var outputData = {};
  const maxColumn = data[0].length;
  for (const row in data) {
    if (row < 1) continue; // 跳過標題列
    const product = data[row];

    const newData = { // 整理資料,將對應欄位轉換為物件
      price: {
        min: product[2],
        max: product[3] || null,
        ori: product[4] || null
      },
      noted: product[1],
      image: product[5],
      description: product[6].toString().split('\n') || null,
      tags: product[7].toString().split('\n') || null,
      options: [],
    }

    for (var i = 8; i < maxColumn; ++i) { // 整理選項資料
      if (product[i]) {
        const optionInfo = product[i].toString().split('\n');
        newData.options.push({
          name: optionInfo[0],
          price: optionInfo[1] || null,
          tag: optionInfo[2] || null
        })
      } else {
        break;
      }
    }

    outputData[`${product[0]}`] = newData; // 將整理後的資料放入物件
  }

  const dataExportFormat = JSON.stringify(outputData); // 輸出 JSON 格式

  return ContentService.createTextOutput(dataExportFormat).setMimeType(ContentService.MimeType.JSON);
}

資料收集 API

javascript
function doPost(e) {
  var contentStr = e.postData.contents;
  var content = doProcess(contentStr); // 將 POST 資料轉換為物件

  var spreadSheet = SpreadsheetApp.openById("試算表ID"); // 請自行替換為實際試算表 ID
  var sheet = spreadSheet.getSheetByName("資料收集"); // 請自行替換為實際工作表名稱
  var range = sheet.getRange("1:1")
  var values = range.getValues()
  var newRow = [];

  var raw = values[0]
  for (var name in raw) {
    if (content[raw[name]]) {
      if (`${content[raw[name]]}`.startsWith('0'))
        newRow.push(("'" + content[raw[name]]).replace(/<br \/>/g, "\n"));
      else
        newRow.push(("" + content[raw[name]]).replace(/<br \/>/g, "\n"));
    }
    else {
      if (raw[name] == "時間")
        newRow.push(new Date(Date.now()).toLocaleString());
      else
        newRow.push("")
    }
  }
  // 新增行
  sheet.appendRow(newRow);

  return HtmlService.createHtmlOutput(`<h1>Done!</h1>`);
}

function doProcess(content) {
  const contentList = content.split(/\r\n/g);
  const contentOutput = {}
  for (const i in contentList) {
    const tmpContent = contentList[i];
    let pos = tmpContent.indexOf('=');
    if (pos > 0)
      contentOutput[tmpContent.substring(0, pos)] = tmpContent.substring(pos + 1).replaceAll(/<br \/>/g, "\n") || '';
  }
  return contentOutput;
}

郵件通知(選用功能)

在資料收集 API 中,可以加入郵件通知功能,當有新的訂單資料時,會自動發送郵件通知。
加入位置:doPost 函式最後(sheet.appendRow(newRow);) 之後,return ... 之前。

javascript
  var edmHTML = HtmlService.createTemplateFromFile('email-content'); // 請自行替換為實際 HTML 檔案名稱
  
  var data = { // 資料預處理
    // 範例資料,請自行替換為實際資料
    name: content["姓名"],
    phone: content["聯絡電話"],
    office: content["系級"],
    price: content["總價"],
    details: ("訂單總覽:<br>" + content["訂單總覽"]),
  };

  // 將資料放入 HTML 檔案
  edmHTML.data = data;
  
  var output = edmHTML.evaluate().getContent();

  // 發送信件
  MailApp.sendEmail({
    to: content["電子郵件"],
    subject: "感謝您填寫竹苗週預購表單", // 郵件主旨,請自行替換
    htmlBody: output
  });

在同一個專案中,新增一個 email-content.html 檔案(或其他名稱,與程式碼中相同即可)。

email-content.html 中,可以自行設計郵件內容,並且使用 data 物件中的資料。
<?= data.name ?> 代表 data 物件中的 name 屬性,以此類推。
<?!= data.details ?> 代表 data 物件中的 details 屬性,並且支援 HTML 標籤,如換行以 <br> 代替。

html
...
<p>姓名:<?= data.name ?></p>
<p>手機:<?= data.phone ?></p>
<p>系級或處室:<?= data.office ?></p>
<p>&nbsp;</p>
<p>總價:<?= data.price ?></p>
<p><?!= data.details ?></p>
...
javascript
data =   const params = {
  "姓名": "123",
  "聯絡電話": '0123456789',
  "系級": 'test',
  "總價": 123,
  "訂單總覽": "123<br>465<br>456789",
  "電子郵件": "test@gmail.com",
  ...
}

替換後

html
...
<p>姓名:123</p>
<p>手機:0123456789</p>
<p>系級或處室:test</p>
<p>&nbsp;</p>
<p>總價:123</p>
<p>訂單總覽:<br>123<br>465<br>456789</p>
...