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

大眼蛙教練點名系統 這篇文章出乎意料地幫助到許多人,因此我在 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 Apps 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 Apps Script)

    想要讓網站編輯試算表需要透過 Google Apps 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:00 10

    GAS 快速教學
    GAS 快速教學

    做好了之後點擊執行▶️,你會需要授予你的程式讀取資料的權限。因為你寫的程式沒有被 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}

    這樣就完成囉

    毛哥EM

    一位喜歡把簡單的事,做得不簡單的高三生。