【GAS】自製點名系統

文章目錄

大眼蛙教練的課程是以堂數做計算,也就是來一次算一次,而不是一段時間。因此每次學生上課都需要簽到,最後還需要人工來統計。因此我做了一個簡單的點名系統,讓電腦來做統計和計算的工作。

大眼蛙教練點名系統

這篇文章出乎意料地幫助到許多人,因此我在2023年重新寫了一次,比上次更容易製作和操作,也更好看一些(我覺得啦)。

建議閱讀這篇文章: https://emtech.cc/post/rol-call/

首頁
大眼蛙教練點名系統
  • 開發者:毛哥EM(我)
  • 類型:網站
  • 網址:EM's Base

功能

新增學生

新增學生
輸入名稱並按新增即可。
電腦會自動在Google Sheet建立好欄位。教練只需要到試算表設定學生的課程數就好了。如果沒有課了會用紅色表示,而未設定會被判斷為沒有課程。
試算表中的學生列表
超重要提醒
  • 學生姓名不可以有空格,如果有空格會自動刪除
  • 要刪除學生請刪除整列,不可留一整列空白

點名

點名畫面
網站會從表單取得學生名單並顯示。只需要點擊名稱即可完成報到。

完成報到後會使用Line Notify通知完成報到的學生和報到時間到指定的群組。

報到成功會提示你還剩下幾堂課
提醒
  • 為了配合不同螢幕大小,沒有限制每行出現的學生數。盡可能多顯示一些,比較容易找到。
  • 為避免重複點名,報到後學生會隱藏。重新整理頁面即可顯現
  • 下方方框會顯示自開啟網頁後已完成報到的學生。
在試算表的「紀錄」可以看到之前學生的報到紀錄。當天的會以綠色做標記。
報到紀錄
超重要提醒
  • 如果要刪除記錄(如誤按到)請務必要「刪除整列」。不可留空

查詢記錄

查詢毛宥恩的報到紀錄
如果要查詢之前報到的紀錄,請到查詢頁面並輸入姓名。

教學

我們分成三個步驟:

  1. 建立試算表(Google Sheet)
  2. 生成Line Notify仗權(若不需要Line通知可省略)
  3. 創建API(Google App Script)來處理資料、發送訊息、以及更新試算表
  4. 建立一個漂亮的網站方便操作

建立試算表(Google Sheet)

我分成兩個表格,分別叫做紀錄統計。為了方便辨識第一排插入標題

紀錄

編號姓名時間剩餘課堂數

統計

學生上課次數剩餘課堂數總課堂數

接著請你複製這個試算表的ID,也就是網址https://docs.google.com/spreadsheets/d//之間那一串(如1fjX-prGu0hfb65LCQkrktWa-JavvjSz7tWMmYWAb7RA)。等一下會用到。

生成Line Notify仗權

Line Notify是個比較冷門但是非常好用的工具。我們可以透過他來從第三方(如你的網站、或是ios捷徑)無限量的廣播訊息到指定的群組,或是單獨發給你。我們會在報到成功後請Line用Line Notify來在群組裡廣播提醒。

請依照圖片步驟建立一個仗權,要給Line看這一串他才知道要傳送信息到哪裡。使用Line Lontify而不是Line機器人的原因是免費版的官方帳號一個月只能傳送500則訊息,但老師的學生數量大,可能會吃不消;且Line Notify設定較簡單。

建立API(Google App Script)

想要讓網站編輯試算表需要透過Google App Script(GAS)來完成。我們要建立四個API,分別用來:

  1. 紀錄出缺席
  2. 獲取學生列表(以進行報到)
  3. 查詢紀錄
  4. 新增學生

到時候我們建立的網站會向這四個API發送請求來更新試算表或獲取資料

提示

google 官方文件 spreadsheet method,裡面有非常詳盡的介紹,包括可以讀取欄位、讀取資料、排序資料、插入資料等等的功能,其實某方面來說算是功能齊全的類資料庫了。有興趣可以點開來看裡面文件。

文件裡面 method 一大堆,還是直接實作比較快。

出缺席紀錄

請建立一個新的專案並貼上以下內容。記得貼上excel那段ID

小叮嚀

為避免程式碼站太多空間,可能會部分隱藏。請記得展開或直接複製。

 1function doGet(e) {
 2  var params = e.parameter;
 3  // 可以針對你帶入的資料變化   params.xxxxxx   xxxxx = 你帶進來的key值  
 4  var name = params.name;
 5  var time = params.time;
 6  var remain = params.remain;
 7  //將Sheet指定為"資料庫"試算表   SpreadSheet = 試算表 ,貼上excel那段參數
 8  var SpreadSheet = SpreadsheetApp.openById("XXXXXXXXXXXXXXXXXXX");
 9  //取得頁籤:"工作表1"    Sheet = 頁籤
10  var Sheet = SpreadSheet.getSheets()[0];
11  //取得有資料的最後一行的"行數"(目的要在最後一行插入新資料)
12  var LastRow = Sheet.getLastRow();
13  // 編號為行數(如要輸入編號2時裡面已經有2行了)
14  var orderNum = LastRow;
15  
16  //開始寫入資料 擋住沒填時間 不給寫入資料,防止被亂撞api
17  if(time !== undefined){
18  //在最後一行的下一行寫入資料
19    Sheet.getRange(LastRow+1, 1).setValue(orderNum);
20    Sheet.getRange(LastRow+1, 2).setValue(name);
21    Sheet.getRange(LastRow+1, 3).setValue(time);
22    Sheet.getRange(LastRow+1, 4).setValue(remain);
23    return ContentService.createTextOutput(true);
24  }
25  // 被亂撞 會回吐這段文字給前端
26  return ContentService.createTextOutput('別亂撞我~ :)');
27}

在這個程式當中,我們說當我們拿著資料到這個應用程式時,將我們給的姓名、時間、剩餘課堂數、以及編號寫入到試算表第一頁最後一行的下一行。但是這樣還沒結束,會後我們還要請Line Notify幫我們廣播。請在return ContentService.createTextOutput(true);之前插入以下程式碼。記得填入剛才生成的仗權。

編輯完成後請按執行。第一次執行時系統會要求你登入Google,請登入現在使用的帳號並提供編輯試算表的權限。Google會告訴你不安全因為這是是你自己製作的應用程式,沒有經過Google審查。直接點選進階,並繼續前往即可。成功部署後請保存應用程式的網址,之後網站就會傳送資料到這個網站來寫入和讀取資料。
完成後可能會看到紅色警告說無法執行,因為我們直接執行了程式,沒有給資料(學生名稱)。因此請建立一個程式碼檔案叫做debug,並貼上以下內容:

 1//呼叫
 2function debug() {
 3    var Result = doGet({
 4        parameter: {
 5            name: '測試先生',
 6            time: '2021/10/10 22:46:00',
 7            remain: 10
 8        }
 9    });
10    Logger.log('Result: %s', Result);
11}

執行後你應該會看到底下顯示執行完畢,且表單多出了一列如下

編號姓名時間剩餘課堂數
1測試先生2021/10/10 22:46:0010
GAS快速教學

做好了之後點擊執行▶️,你會需要授予你的程式讀取資料的權限。因為你寫的程式沒有被Google驗證過所以會顯示不安全,但我相信你不會把你的帳號搞爆,對吧

接下來我們要部署它,讓它成為一個網站來讓我們抓。這裡選擇網頁應用程式,所有人都以你的身份讀取。按下部署就可以囉

這裡我們把部署的網址複製起來。如果要做修改除了按儲存之外要記得重新部署成新版本才會更新喔

小叮嚀

若發布後還有做修改,既得要再次發布且要發布為新版本。

學生列表

學生列表不需要輸入,直接讀取內容就好了。這裡使用的輸出格式是JSON。JSON就是ios捷徑APP裡的辭典,簡單來說就是一個對照表。比如說你想要紀錄一個人的基本資料如下

 1{
 2     "姓": "毛",
 3     "名": "宥鈞",
 4     "性別": "男",
 5     "年齡": 15,
 6     "住址": 
 7     {
 8         "路名": "大馬路",
 9         "city": "台中市",
10         "國家": "台灣",
11         "郵遞區號": "40763"
12     }
13}

我們可以輕鬆的讓JavaScript讀懂它。請以相同方式建立以下API

 1function doGet(e){
 2  var id = 'XXXXXXXXXXXXXXXXXXX'; //抓取表單
 3  var spreadsheet = SpreadsheetApp.openById(id); // Sheet id
 4  var sheet = spreadsheet.getSheets()[1]; // 要第幾個sheet? 1 就是第2個
 5  var rowLength = sheet.getLastRow()-1; //取行長度
 6  var columnLength = sheet.getLastColumn(); //取列長度
 7  var data = sheet.getRange(2,1,rowLength,columnLength).getValues(); // 取得的資料
 8  var dataExport = {};
 9  // 一個個加入json
10  for(i in data){
11    if(data[i][0] != ""){
12    dataExport[i] = {
13      name:   data[i][0],
14      left:   data[i][2],
15      };
16    };
17  };
18  // 回傳JSON
19  var dataExportFormat = JSON.stringify(dataExport);
20  return ContentService.createTextOutput(dataExportFormat).setMimeType(ContentService.MimeType.JSON);
21  }

在這段程式當中,我們一列一列的把試算表的資料塞進JSON裡,最後再回傳給我們。

查詢紀錄

這裡使用Post來傳送而不是Get。其實都可以,只是想說換一個方式。差別在於使用Get時資料是存在網址當中,而Post像是還有一個附件。因為資料量很小,所以都可以使用。

 1function doPost(e) {
 2    var params = e.parameter;
 3      // 可以針對你帶入的資料變化   params.xxxxxx   xxxxx = 你帶進來的key值
 4    var name = params.name;
 5    var SpreadSheet = SpreadsheetApp.openById('XXXXXXXXXXXXXXXXXXX'); //抓取表單
 6    var Sheet = SpreadSheet.getSheets()[0];
 7    var LastRow = Sheet.getLastRow();
 8    var data = [];
 9    var listAll = Sheet.getSheetValues(1, 2, LastRow, 4);
10    // 把符合的抓出來
11    for (var i = 0; i < listAll.length; i++) {
12        if (listAll[i].indexOf(name) === 0) {
13            data.push({ data: listAll[i], index: i + 1 });
14        }
15    }
16    // 回傳JSON
17    return ContentService.createTextOutput(JSON.stringify(data)).setMimeType(
18        ContentService.MimeType.JSON
19    );
20}

在這段程式當中雖然一樣是回傳資料,但是在塞進JSON前先判斷一下姓名是否符合。直得注意的是我們是從第2欄開始抓,因為使用者不需要知道這是所有資料當中的第幾筆資料。

新增學生

 1function doGet(e) {
 2  var params = e.parameter;
 3  // 可以針對你帶入的資料變化  params.xxxxxx      xxxxx = 你帶進來的key值  
 4  var name = params.name;
 5  //將Sheet指定為"資料庫"試算表   SpreadSheet = 試算表 ,貼上excel那段參數
 6  var SpreadSheet = SpreadsheetApp.openById("XXXXXXXXXXXXXXXXXXX");
 7  //取得頁籤:"工作表1"              Sheet = 頁籤
 8  var Sheet = SpreadSheet.getSheets()[1];
 9  //取得有資料的最後一行的"行數"(目的要在最後一行插入新資料)
10  var LastRow = Sheet.getLastRow();
11  var now = LastRow+1;
12 //格字內加入函式統計課程數
13  var his="=COUNTIF('紀錄'!B:B,A"+now+")";
14  //格字內加入函式計算剩餘課程數
15  var less='=D'+now+'-B'+now;
16  if(name !== undefined){
17    //在最後一行的下一行寫入資料
18    Sheet.getRange(LastRow+1, 1).setValue(name);
19    Sheet.getRange(LastRow+1, 2).setValue(his);
20    Sheet.getRange(LastRow+1, 3).setValue(less);
21    return ContentService.createTextOutput(true);
22  }
23  // 被亂撞 會回吐這段文字給前端
24  return ContentService.createTextOutput('別亂撞我~ :)');
25}

我們在其中一個儲存格中插入了一個叫做COUNTIF的函式。他會統計在紀錄當中有幾筆資料的姓名和他左邊的姓名一樣。而剩餘課堂數就是全部課堂數減統計出來已經上的課堂數。

建立網站

最後,讓我們來做一個的簡單漂亮的網站吧。 請選一個地方建立以下幾個純文字檔案

  • check-in.html
  • search.html
  • sign-up.html
  • index.html
  • style.css

HTML是網頁的檔案,有點像Word檔,而CSS是用來裝飾HTML的。你可以用它來決定字要多大、什麼顏色、間距要多少等。

而我們在HTML檔中還插入了一些JavaScript來傳送和讀取資料和顯示資料。我使用了一個叫做jQuery的JavaScript函式庫,它可以讓程式變得更簡略。記得把傳送到的網址改成你剛才建的Google App Script的網址,其他你可以直接複製貼上。

index.html

 1<!DOCTYPE html>
 2<head>
 3 <meta charset="utf-8" />
 4 <title>點名系統</title>
 5 <!-- 網站資訊 -->
 6 <meta name="description" content="使用Google sheet的api紀錄出缺席" />
 7 <meta name="author" content="毛哥EM" />
 8 <!-- 讓網址正常顯示以及裝飾 -->
 9 <meta name="viewport" content="width=device-width, initial-scale=1" />
10 <meta name="theme-color" content="00BFFF" />
11 <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
12</head>
13<body>
14 <main>
15  <h1>點名系統</h1>
16  <p>主選單</p>
17  <button onclick="window.location='check-in.html';">報到</button>
18  <button onclick="window.location='search.html';">查詢紀錄</button>
19  <button onclick="window.location='sign-up.html';">新增學生</button>
20  <p>
21   <a href="https://Edit-Mr.github.io">毛哥EM</a>製作
22  </p>
23 </main>
24</body>

sign-up.html

 1<!DOCTYPE html>
 2<head>
 3 <meta charset="utf-8" />
 4 <title>新增學生 - 點名系統</title>
 5 <!-- 網站資訊 -->
 6 <meta name="description" content="使用Google sheet的api紀錄出缺席" />
 7 <meta name="author" content="毛哥EM" />
 8 <!-- 讓網址正常顯示以及裝飾 -->
 9 <meta name="viewport" content="width=device-width, initial-scale=1" />
10 <meta name="theme-color" content="00BFFF" />
11 <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
12 <!-- 載入jQuery -->
13 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
14</head>
15<body>
16 <main>
17  <h1><a href="index.html">點名系統</a></h1>
18  <p>新增學生</p>
19  <input type="text" class="form-control" id="name" />
20  <button type="button" id="sendOrder">新增</button>
21  <p>
22   <a href="https://Edit-Mr.github.io">毛哥EM</a>製作<br />
23   送出後請稍等數秒 勿重複新增
24  </p>
25 </main>
26 <script>
27  $(function () {
28      // 監聽 按鈕點擊
29  
30      $("#sendOrder").click(function (e) {
31          // 姓名
32  
33          var name = $("#name").val();
34  
35          $("input").focus(function () {
36              $(this).css("border", "");
37          });
38  
39          // 擋住不填資料
40  
41          if (name == "") {
42              $("#name").css("border", "1px solid #ff0000");
43          } else {
44              var name = name.replace(" ", "");
45  
46              var data = {
47                  name: name,
48              };
49  
50              $.ajax({
51                  // 這邊用get type
52  
53                  type: "get",
54  
55                  // api url - google appscript 產出的 url
56  
57                  url: "https://script.google.com/............",
58  
59                  // 剛剛整理好的資料帶入
60  
61                  data: data,
62  
63                  // 資料格式是JSON
64  
65                  dataType: "JSON",
66  
67                  // 成功送出 會回頭觸發下面這塊
68  
69                  success: function (response) {
70                      console.log(response);
71  
72                      alert("新增成功!!");
73                  },
74              });
75          }
76      });
77  });
78 </script>
79</body>

check-in.html

 1<!DOCTYPE html>
 2<head>
 3 <meta charset="utf-8" />
 4 <title>報到 - 點名系統</title>
 5 <!-- 網站資訊 -->
 6 <meta name="description" content="使用Google sheet的api紀錄出缺席" />
 7 <meta name="author" content="毛哥EM" />
 8 <!-- 讓網址正常顯示已經裝飾 -->
 9 <meta name="viewport" content="width=device-width, initial-scale=1" />
10 <meta name="theme-color" content="00BFFF" />
11 <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
12 <!-- 載入jQuery和學生列表 -->
13 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
14</head>
15<script>
16 //用Get讀取資料
17    window.onload = () => {
18     // api url - google appscript 產出的 url
19        let requestURL = "https://script.google.com/.........";
20        let request = new XMLHttpRequest();
21        request.open("GET", requestURL);
22        request.responseType = "json";
23        request.send();
24        //收到資料後輪流做成按鈕
25        request.onload = function () {
26            let student = request.response;
27            for (var i in student) {
28                var now = student[i];
29                var stu = now.name;
30                var left = now.left;
31                --left;
32                var but = '<button id="' + stu + '">' + stu + "</button>";
33                console.log(but);
34                $("#students").append(but);
35                var iden = "#" + stu;
36                var click = 'to("' + stu + '", ' + left + ");";
37                $(iden).attr("onclick", click);
38            }
39            //載入完成後更改副標題
40            $("#header").text("點擊姓名即可完成報到");
41        };
42    };
43    function to(name, have) {
44        var currentdate = new Date();
45        var filltime = currentdate.getFullYear() + "/" + (currentdate.getMonth() + 1) + "/" + currentdate.getDate() + "  " + currentdate.getHours() + ":" + currentdate.getMinutes() + ":" + currentdate.getSeconds();
46        // 打包 要的資料
47        var course = have;
48        console.log(course);
49        var data = {
50            name: name,
51            time: filltime,
52            remain: course,
53        };
54        var tag = name;
55        $.ajax({
56            // 這邊用get type
57            type: "get",
58            // api url - google appscript 產出的 url
59            url: "https://script.google.com/.........",
60            // 剛剛整理好的資料帶入
61            data: data,
62            // 資料格式是JSON
63            dataType: "JSON",
64            // 成功送出 會回頭觸發下面這塊
65            success: function (response) {
66                var msg = response;
67                alert("報到成功! 還剩" + course + "堂課");
68            },
69        });
70        //報到完成的顯示在下方框框並將按鈕隱藏
71        $("#ed").prepend("<li>" + tag + "</li>");
72        var id = "#" + tag;
73        $(id).fadeOut();
74    }
75</script>
76</head>
77<body>
78 <main>
79  <h1><a href="index.html">點名系統</a></h1>
80  <p id="header">載入中</p>
81  <div id="students"></div>
82  <h2>已到學生</h2>
83  <p class="ed" id="ed"></p>
84  <p><a href="https://Edit-Mr.github.io">毛哥EM</a>製作</p>
85 </main>
86</body>

search.html

 1<!DOCTYPE html>
 2<head>
 3    <meta charset="utf-8" />
 4    <title>查詢 - 點名系統</title>
 5    <!-- 網站資訊 -->
 6    <meta name="description" content="使用Google sheet的api紀錄出缺席" />
 7    <meta name="author" content="毛哥EM" />
 8    <!-- 讓網址正常顯示以及裝飾 -->
 9    <meta name="viewport" content="width=device-width, initial-scale=1" />
10    <meta name="theme-color" content="00BFFF" />
11    <link rel="stylesheet" type="text/css" href="style.css" media="screen" />
12    <!-- 載入jQuery -->
13    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
14</head>
15<body>
16    <main>
17        <h1><a href="index.html">點名系統</a></h1>
18        <p>紀錄查詢</p>
19        <input type="text" id="name" />
20        <button type="button" id="send">查詢</button>
21        <!-- 顯示查詢結果的表格 先隱藏 -->
22        <table class="table table-striped" style="display: none;">
23            <thead>
24                <tr>
25                    <th>姓名</th>
26                    <th>報到時間</th>
27                    <th>剩下課堂</th>
28                </tr>
29            </thead>
30            <tbody id="dataView">
31                <!-- 查詢到的資料會插入到這裡 -->
32            </tbody>
33        </table>
34        <p>
35            <a href="https://Edit-Mr.github.io">毛哥EM</a>製作<br />
36            送出後請稍等數秒
37        </p>
38    </main>
39    <script>
40        //當傳送按鈕被點擊
41        $(function () {
42            $("#send").click(function (e) {
43                var status = true;
44                var name = $("#name").val();
45                $("input").focus(function () {
46                    $(this).css("border", "");
47                });
48                //擋住沒有輸入姓名
49                if (name == "") {
50                    $("#name").css("border", "1px solid #ff0000");
51                } else {
52                    var data = {
53                        name: name,
54                    };
55                    //Post到前面做的API,記得更改網址
56                    $.ajax({
57                        // 這邊用post type
58                        type: "post",
59                        // api url - google appscript 產出的 url
60                        url: "https://script.google.com/...........",
61                        data: data,
62                        dataType: "JSON",
63                        success: function (response) {
64                            var content = "";
65                            //把資料一行行做出表格
66                            response.forEach((element) => {
67                                var [name, time, remain] = element.data;
68                                var index = element.index;
69                                content += `<tr>
70                <td>${name}</td>
71                <td>${new Date(time)}</td>
72                <td>${remain}</td>
73              </tr>`;
74                            });
75                            //如果有資料的話顯示表格,否則提示查無資料
76                            if (content) {
77                                document.getElementsByClassName("table-striped")[0].style.display = "table";
78                            } else {
79                                alert("查無資料");
80                            }
81                            var dataView = document.getElementById("dataView");
82                            dataView.innerHTML = content;
83                            $("#name").val("");
84                        },
85                    });
86                }
87            });
88        });
89    </script>
90</body>

style.css

 1@charset "utf-8";
 2/*按鈕
 3-----------------*/
 4button {
 5    outline: 0;
 6    font-size: 1.5em;
 7    font-weight: 600;
 8    background: #fff;
 9    border: none;
10    margin: 5px;
11    padding: 0.5em;
12    transition: all 0.3s ease-out;
13    box-shadow: inset 0 -8px 0 0 rgba(0, 0, 0, 0.2), 1px 1px 0 0 deepskyblue, 2px 2px 0 0 deepskyblue, 3px 3px 0 0 deepskyblue, 4px 4px 0 0 deepskyblue, 5px 5px 0 0 deepskyblue, 6px 6px 0 0 deepskyblue, 7px 7px 0 0 deepskyblue,
14        8px 8px 0 0 deepskyblue, 9px 9px 0 0 deepskyblue, 10px 10px 0 0 deepskyblue, 11px 11px 0 0 deepskyblue, 12px 12px 0 0 deepskyblue;
15}
16button:hover {
17    color: #444;
18    box-shadow: inset 0 -4px 0 0 rgba(0, 0, 0, 0.2), 1px 1px 0 0 deepskyblue, 2px 2px 0 0 deepskyblue, 3px 3px 0 0 deepskyblue, 4px 4px 0 0 deepskyblue, 5px 5px 0 0 deepskyblue;
19}
20button:active {
21    color: #222;
22}
23/*其他
24-----------------*/
25a {
26    color: white;
27    text-decoration: none;
28}
29a:hover {
30    text-decoration: underline;
31}
32.ed {
33    padding: 5px;
34    border: 5px white solid;
35    border-radius: 10px;
36}
37input {
38    outline: 0;
39    font-size: 1.5em;
40    font-weight: 600;
41    background: #fff;
42    border: none;
43    box-shadow: inset 0 -4px 0 0 rgba(0, 0, 0, 0.2), 1px 1px 0 0 deepskyblue, 2px 2px 0 0 deepskyblue, 3px 3px 0 0 deepskyblue, 4px 4px 0 0 deepskyblue, 5px 5px 0 0 deepskyblue;
44    margin: 5px;
45    padding: 0.5em;
46    transition: all 0.3s ease-out;
47    box-shadow: inset 0 -8px 0 0 rgba(0, 0, 0, 0.2), 1px 1px 0 0 deepskyblue, 2px 2px 0 0 deepskyblue, 3px 3px 0 0 deepskyblue, 4px 4px 0 0 deepskyblue, 5px 5px 0 0 deepskyblue, 6px 6px 0 0 deepskyblue, 7px 7px 0 0 deepskyblue,
48        8px 8px 0 0 deepskyblue, 9px 9px 0 0 deepskyblue, 10px 10px 0 0 deepskyblue, 11px 11px 0 0 deepskyblue, 12px 12px 0 0 deepskyblue;
49}
50body {
51    background-color: lightskyblue;
52    color: white;
53}
54main {
55    position: relative;
56    margin: auto;
57    width: 100%;
58    height: 100%;
59    text-align: center;
60}
61h1 {
62    margin-bottom: -10px;
63}
64
65table {
66    margin-top: 30px;
67    color: black;
68    border-spacing: 1;
69    border-collapse: collapse;
70    background: white;
71    border-radius: 6px;
72    max-width: 100%;
73    width: 100%;
74}
75table * {
76    position: relative;
77}
78table td,
79table th {
80    padding-left: 8px;
81}
82table thead tr {
83    height: 60px;
84    background: deepskyblue;
85    font-size: 16px;
86}
87table tbody tr {
88    height: 48px;
89    border-bottom: 1px solid #e3f1d5;
90}

這樣就完成囉

Posts in this Series