【GAS】自製點名系統
大眼蛙教練的課程是以堂數做計算,也就是來一次算一次,而不是一段時間。因此每次學生上課都需要簽到,最後還需要人工來統計。因此我做了一個簡單的點名系統,讓電腦來做統計和計算的工作。
大眼蛙教練點名系統 這篇文章出乎意料地幫助到許多人,因此我在 2023 年重新寫了一次,比上次更容易製作和操作,也更好看一些 (我覺得啦)。
建議閱讀這篇文章:https://emtech.cc/post/rol-call/
大眼蛙教練點名系統
- 開發者:毛哥EM(我)
- 類型:網站
- 網址:EM's Base
功能
新增學生
輸入名稱並按新增即可。
電腦會自動在 Google Sheet 建立好欄位。教練只需要到試算表設定學生的課程數就好了。如果沒有課了會用紅色表示,而未設定會被判斷為沒有課程。
超重要提醒
- 學生姓名不可以有空格,如果有空格會自動刪除
- 要刪除學生請刪除整列,不可留一整列空白
點名
網站會從表單取得學生名單並顯示。只需要點擊名稱即可完成報到。
完成報到後會使用 Line Notify 通知完成報到的學生和報到時間到指定的群組。
提醒
-
為了配合不同螢幕大小,沒有限制每行出現的學生數。盡可能多顯示一些,比較容易找到。
-
為避免重複點名,報到後學生會隱藏。重新整理頁面即可顯現
-
下方方框會顯示自開啟網頁後已完成報到的學生。
在試算表的「紀錄」可以看到之前學生的報到紀錄。當天的會以綠色做標記。
超重要提醒
如果要刪除記錄(如誤按到)請務必要「刪除整列」。不可留空
查詢記錄
如果要查詢之前報到的紀錄,請到查詢頁面並輸入姓名。
教學
我們分成三個步驟:
- 建立試算表 (Google Sheet)
- 生成 Line Notify 仗權 (若不需要 Line 通知可省略)
- 創建 API(Google Apps Script) 來處理資料、發送訊息、以及更新試算表
- 建立一個漂亮的網站方便操作
建立試算表 (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 Apps Script)
想要讓網站編輯試算表需要透過 Google Apps Script(GAS) 來完成。我們要建立四個 API,分別用來:
- 紀錄出缺席
- 獲取學生列表 (以進行報到)
- 查詢紀錄
- 新增學生
到時候我們建立的網站會向這四個 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:00 | 10 |
做好了之後點擊執行▶️,你會需要授予你的程式讀取資料的權限。因為你寫的程式沒有被 Google 驗證過所以會顯示不安全,但我相信你不會把你的帳號搞爆,對吧
接下來我們要部署它,讓它成為一個網站來讓我們抓。這裡選擇網頁應用程式,所有人都以你的身份讀取。按下部署就可以囉
這裡我們把部署的網址複製起來。如果要做修改除了按儲存之外要記得重新部署成新版本才會更新喔
小叮嚀 若發布後還有做修改,既得要再次發布且要發布為新版本。
學生列表
學生列表不需要輸入,直接讀取內容就好了。這裡使用的輸出格式是 JSON。JSON 就是 ios 捷徑 APP 裡的辭典,簡單來說就是一個對照表。比如說你想要紀錄一個人的基本資料如下
1{
2 "姓": "毛",
3 "名": "宥鈞",
4 "性別": "男",
5 "年齡": 15,
6 "住址": {
7 "路名": "大馬路",
8 "city": "台中市",
9 "國家": "台灣",
10 "郵遞區號": "40763"
11 }
12}
我們可以輕鬆的讓 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(
21 ContentService.MimeType.JSON
22 );
23}
在這段程式當中,我們一列一列的把試算表的資料塞進 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 Apps 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><a href="https://Edit-Mr.github.io">毛哥EM</a>製作</p>
21 </main>
22</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
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
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
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(
78 "table-striped"
79 )[0].style.display = "table";
80 } else {
81 alert("查無資料");
82 }
83 var dataView = document.getElementById("dataView");
84 dataView.innerHTML = content;
85 $("#name").val("");
86 }
87 });
88 }
89 });
90 });
91 </script>
92</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:
14 inset 0 -8px 0 0 rgba(0, 0, 0, 0.2),
15 1px 1px 0 0 deepskyblue,
16 2px 2px 0 0 deepskyblue,
17 3px 3px 0 0 deepskyblue,
18 4px 4px 0 0 deepskyblue,
19 5px 5px 0 0 deepskyblue,
20 6px 6px 0 0 deepskyblue,
21 7px 7px 0 0 deepskyblue,
22 8px 8px 0 0 deepskyblue,
23 9px 9px 0 0 deepskyblue,
24 10px 10px 0 0 deepskyblue,
25 11px 11px 0 0 deepskyblue,
26 12px 12px 0 0 deepskyblue;
27}
28button:hover {
29 color: #444;
30 box-shadow:
31 inset 0 -4px 0 0 rgba(0, 0, 0, 0.2),
32 1px 1px 0 0 deepskyblue,
33 2px 2px 0 0 deepskyblue,
34 3px 3px 0 0 deepskyblue,
35 4px 4px 0 0 deepskyblue,
36 5px 5px 0 0 deepskyblue;
37}
38button:active {
39 color: #222;
40}
41/*其他
42-----------------*/
43a {
44 color: white;
45 text-decoration: none;
46}
47a:hover {
48 text-decoration: underline;
49}
50.ed {
51 padding: 5px;
52 border: 5px white solid;
53 border-radius: 10px;
54}
55input {
56 outline: 0;
57 font-size: 1.5em;
58 font-weight: 600;
59 background: #fff;
60 border: none;
61 box-shadow:
62 inset 0 -4px 0 0 rgba(0, 0, 0, 0.2),
63 1px 1px 0 0 deepskyblue,
64 2px 2px 0 0 deepskyblue,
65 3px 3px 0 0 deepskyblue,
66 4px 4px 0 0 deepskyblue,
67 5px 5px 0 0 deepskyblue;
68 margin: 5px;
69 padding: 0.5em;
70 transition: all 0.3s ease-out;
71 box-shadow:
72 inset 0 -8px 0 0 rgba(0, 0, 0, 0.2),
73 1px 1px 0 0 deepskyblue,
74 2px 2px 0 0 deepskyblue,
75 3px 3px 0 0 deepskyblue,
76 4px 4px 0 0 deepskyblue,
77 5px 5px 0 0 deepskyblue,
78 6px 6px 0 0 deepskyblue,
79 7px 7px 0 0 deepskyblue,
80 8px 8px 0 0 deepskyblue,
81 9px 9px 0 0 deepskyblue,
82 10px 10px 0 0 deepskyblue,
83 11px 11px 0 0 deepskyblue,
84 12px 12px 0 0 deepskyblue;
85}
86body {
87 background-color: lightskyblue;
88 color: white;
89}
90main {
91 position: relative;
92 margin: auto;
93 width: 100%;
94 height: 100%;
95 text-align: center;
96}
97h1 {
98 margin-bottom: -10px;
99}
100
101table {
102 margin-top: 30px;
103 color: black;
104 border-spacing: 1;
105 border-collapse: collapse;
106 background: white;
107 border-radius: 6px;
108 max-width: 100%;
109 width: 100%;
110}
111table * {
112 position: relative;
113}
114table td,
115table th {
116 padding-left: 8px;
117}
118table thead tr {
119 height: 60px;
120 background: deepskyblue;
121 font-size: 16px;
122}
123table tbody tr {
124 height: 48px;
125 border-bottom: 1px solid #e3f1d5;
126}
這樣就完成囉