iT邦幫忙

0

國軍放假自動彙整回報網頁

  • 分享至 

  • xImage
  •  

前言

待過軍事訓練役的人肯定有著假日還要忙著回報休假狀況,而回報由於還是在line裡面,要馬就是要麻煩班頭整理,要馬就是在那邊卡來卡去,實在是麻煩至極,於是我趁中間的假日開始思考什麼樣的流程可以優化這個操作呢,最直觀想到的當然是linebot,讓每個人在回覆時下達指令,後端幫忙整理,最後再用特定指令叫出來,但實際寫出來給鄰兵試用後,發現全文字介面以及死板的指令操作造成相當大的負面回應,於是開始往具有圖形介面的googleSheet編輯,再利用lineBot調用googleSheetAPI的方案走。結果遇到由於共編手機板需要下載,以及看起來弱弱的、......等原因導致推廣失敗。最後決定使用網頁做為載體,提供單純的入口以及圖形化與特化的操作,終於成功推廣,以下進行技術說明

環境架構與技術

後端環境 前端環境 後端框架 前端框架 資料庫 資料庫介面
node line瀏覽器 express boostrap mongodb mongoClient(mongoDB原生)

使用者操作流程

https://ithelp.ithome.com.tw/upload/images/20201219/20131164es4onCHNhx.png

實際畫面

設定頁

班級專屬頁

設定班級成員範例圖

班級代號規則如文字說明,僅連支援中文,支持三個回報時間,進入班級頁時會自動挑選下一個時間,下方按照希望呈現的順序輸入成員號碼及敘述文字,最後案創建即可,不支援修改,所以創建者創建需一步到位,不然得由管理者於資料庫中進行手動刪除。

創建成功後取得專屬網址

輸入代號按進入,確定呈現內容無誤即可複製該網頁網址到line定為公告。

複製網址

技術說明

引用express框架


利用Visual Studio 2019 community 引用Express4框架

加載額外套件以及架構預設設定變更

加載額外套件

  1. 右鍵
  2. 安裝mongodb(mongoClient)

架構預設設定變更

  1. 畫面渲染引擎為ejs,頁面副檔名設為html,預設為別的,在這邊進行修改
// view engine setup
app.engine('.html', require('ejs').__express)
app.set('views', path.join(__dirname, 'views')); //注意path要require一下
app.set('view engine', 'html')
  1. 最後的app.js(要改的只有上面一步和刪除用不到的路由,其他都是visual studio自己生成的)
'use strict';
var debug = require('debug');
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');

var app = express();

// view engine setup
app.engine('.html', require('ejs').__express)
app.set('views', path.join(__dirname, 'views')); //注意path要require一下
app.set('view engine', 'html')

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
//// catch 404 and forward to error handler
//app.use(function (req, res, next) {
//    var err = new Error('Not Found');
//    err.status = 404;
//    next(err);
//});

//// error handlers

//// development error handler
//// will print stacktrace
//if (app.get('env') === 'development') {
//    app.use(function (err, req, res, next) {
//        res.status(err.status || 500);
//        res.render('error', {
//            message: err.message,
//            error: err
//        });
//    });
//}

//// production error handler
//// no stacktraces leaked to user
//app.use(function (err, req, res, next) {
//    res.status(err.status || 500);
//    res.render('error', {
//        message: err.message,
//        error: {}
//    });
//});

app.set('port', process.env.PORT || 1451);

var server = app.listen(app.get('port'), function () {
    debug('Express server listening on port ' + server.address().port);
    console.log(process.env.PORT || 1451);
});

檔案架構



  • routes
    • index.js
  • views
    • error.html
    • index.html
    • set.html
  • app.js

後端路由(index.js)

資料庫

建立一個庫來儲存所有班級資料

  • db: armyUsers
    • collections: 班級代號(按照上面提過的命名規則)
      • element:成員座號=>ex:30(在此設定前方的0都要省略,所以不會有030)
      • element:成員敘述=>ex:31030 王大明(學號+空格+姓名)

每個班級有一個庫可以儲存每次回報的內容

  • dbs:班級代號(按照上面提過的命名規則)
    • collections: 年\月\日 時(ex:109/10/10 11時回報)
      • elements:回報內容=>30:1000 在家睡覺(座號:在做的事)

API

  1. 網站進入點
    共有2句API提供給使用者進入設定頁或班級專屬頁
//the URL that we can connect to this Web(door of this Web)
//進入班級專屬頁
router.get("/index/:token", function (req, res) {
    res.render("index", { token: req.params.token });
});
//進入設定頁
router.get("/", function (req, res) {
    res.render("set", {});
});

班級專屬頁為了使一句API提供給多個班級,使用了動態路由的技巧
2. 創建新班級

router.post("/buildClass", function (req, res) {
    //name:collection name, data: the elements in collections, it is a string can be split by <-> and <_>
    const name = req.body.name;
    const data = req.body.data;//num_include-num_include-..........-num_include

    MongoClient.connect(url, function (err, client) {
        if (err) throw err;
        IsExistCollection(name, client)
            .then(bool => insertClassData(name, data, client))
            .then(bool => res.end("success"))
            .catch(bool => res.end("error"))
            .finally(bool => client.close())
    });
});
//check if this class have exist
function IsExistCollection(name, client) {
    return new Promise((resolve, reject) => {
        var db = client.db(dbUsers);
        db.listCollections({ name: name })
            .next(function (err, collinfo) {
                err ? reject(false) : (collinfo ? reject(true) : resolve(false));
            });
    });
}
//record all users in the class
function insertClassData(name, data, client) {
    return new Promise((resolve, reject) => {
        var table = client.db(dbUsers).collection(name);
        //split "data"
        var numList = [];
        var includeList = [];
        data.split("-").forEach(element => {
            numList.push(element.split("_")[0]);
            includeList.push(element.split("_")[1]);
        });
        var jsonList = [];
        for (var i in numList) {
            var json = {};
            json["num"] = numList[i];
            json["include"] = includeList[i];
            jsonList.push(json);
        }
        table.insertMany(jsonList, function (err, result) {
            err ? reject(false) : resolve(true);
        })
    });
}

該API接受兩個參數,前者為獲取該創建班級的代號,後者為獲取班級所有使用者的號碼以及敘述(格式於上方註解中有說明)
做法是先檢查有沒有已創建該班級,可以藉由檢查armyUsers內有沒有collection名子為該班及代號來完成。
下一步是把第二個參數拆成jsonList插入資料庫,collection名為班級代號。
3. 個人進行回報

router.post("/send", function (req, res) {
    const token = req.body.token;//each class have it's own db to save data,db's name is it's token
    const when = req.body.when;
    const who = req.body.who;
    const what = req.body.what;

    MongoClient.connect(url, function (err, client) {
        if (err) throw err;
        //console.log("Connected successfully to server");
        const db = client.db(token);
        const collection = db.collection(when);
        // Insert some documents
        collection.updateOne({ num: who }, { $set: { num: who, include: what } }, { upsert: true }, function (err, result) {
            if (err) res.send("error");
            else {
                client.close();
                res.send("success");
            }
        });
    });
});

該API接受以下4個參數

  • 班級代號
  • 回報時間節點
  • 回報者座號
  • 回報內容

做法是對名稱為'班級代號'的DB,名稱為'回報時間節點'的collection,更新一個document,內容含'回報者座號'及'回報內容'
而且要設為更新,讓使用者可以進行修改,upsert要設為true,這樣沒有得更新時才能改為插入。
4. 刷新班級看板

router.post("/refresh", function (req, res) {
    const token = req.body.token;//each class have it's own db to save data,db's name is it's token
    const when = req.body.when;
    //console.log(req.body);
    MongoClient.connect(url, function (err, client) {
        if (err) throw "error";
        getUsers(token, client)
            .then(pkg => getResponse(pkg, token, when, client))
            .then(re => res.send(re))
            .catch(error => res.send(error))
            .finally(re => client.close())
    });
});

function getUsers(token, client) {
    return new Promise((resolve, reject) => {
        var table = client.db(dbUsers).collection(token);
        table.find({}).sort({ _id : 1 }).toArray(function (err, result) {
            err ? reject({ result: "connect error" }) : resolve(result);
        })
    });
}

function getResponse(pkg,token,when,client) {
    return new Promise((resolve, reject) => {
        var table = client.db(token).collection(when);
        table.find({}).toArray(function (err, result) {
            if (err)
                reject("connect error");
            else {
                var json = {};
                for (var i in result) json[result[i].num] = result[i].include;
                var str = "";
                for (var i in pkg)
                    str += "\n" + pkg[i].include + " : " + (json[pkg[i].num] != null ? json[pkg[i].num] : '<strong style="background-color: gray;">尚未回覆</strong>');
                //console.log(reply(token, when, result.length, str));
                //console.log("reply");
                resolve(reply(token.split('~'), when, result.length, str));
            }
        })
    });
}

function reply(token,when, length, str) {
    return (
        when +
        "\n" + decodeURI(token[1]).toString() + "連訓員 第" + token[2] + "班\n今日看診人員:共0員\n發燒人員:共0員\n應到:" + token[3] + "員 \n實到:" +
        length +
        "員" +
        str
    );
}

這句API接受班級代號與時間節點兩個參數,
作法是先到armyUsers找到名稱為班級代號的collection取得班級所有設定資料
,再到名稱為'班級代號'的DB,名稱為'回報時間節點'的collection取得該班該時間節點的回報訊息
組合這兩個資訊進行排序再回傳結果字串。

完整後端(源碼)

要自己加入資料庫連結
'use strict';
var express = require('express');
var router = express.Router();
const MongoClient = require("mongodb").MongoClient;
// Connection URL
//local mongoDB URL
//const url = "mongodb://localhost:27017";
//cloud mongoDB URL
const url = "這裡要放雲端mongoDB的連結URL"
// Database Name
const dbUsers = "armyUsers";

//the URL that we can connect to this Web(door of this Web)
router.get("/index/:token", function (req, res) {
    res.render("index", { token: req.params.token });
});
router.get("/", function (req, res) {
    res.render("set", {});
});
/******************************************************
post: buildClass ,use it to build a collection which can let Web know who are in the class
db:armyUsers collection: (營)~(連)~(班)~(人數)~(第一時間)~(第二時間)~(第三時間)=>班級編號 element: num=>30, include=>31030 林小明
token: ex:3~步一~10~17~11~14~19 => (營)~(連)~(班)~(人數)~(第一時間)~(第二時間)~(第三時間)
*******************************************************/
router.post("/buildClass", function (req, res) {
    //name:collection name, data: the elements in collections, it is a string can be split by <-> and <_>
    const name = req.body.name;
    const data = req.body.data;//num_include-num_include-..........-num_include

    MongoClient.connect(url, function (err, client) {
        if (err) throw err;
        IsExistCollection(name, client)
            .then(bool => insertClassData(name, data, client))
            .then(bool => res.end("success"))
            .catch(bool => res.end("error"))
    });
});
//check if this class have exist
function IsExistCollection(name, client) {
    return new Promise((resolve, reject) => {
        var db = client.db(dbUsers);
        db.listCollections({ name: name })
            .next(function (err, collinfo) {
                err ? reject(false) : (collinfo ? reject(true) : resolve(false));
            });
    });
}
//record all users in the class
function insertClassData(name, data, client) {
    return new Promise((resolve, reject) => {
        var table = client.db(dbUsers).collection(name);
        //split "data"
        var numList = [];
        var includeList = [];
        data.split("-").forEach(element => {
            numList.push(element.split("_")[0]);
            includeList.push(element.split("_")[1]);
        });
        var jsonList = [];
        for (var i in numList) {
            var json = {};
            json["num"] = numList[i];
            json["include"] = includeList[i];
            jsonList.push(json);
        }
        table.insertMany(jsonList, function (err, result) {
            err ? reject(false) : resolve(true);
        })
    });
}
/*****************************************
 post:send Record => 'when'? 'who' do "what"
 db:army collection:109/XX/XX XX點回報 element: num=>30,include=>1000在家睡覺
  *****************************************/

router.post("/send", function (req, res) {
    const token = req.body.token;//each class have it's own db to save data,db's name is it's token
    const when = req.body.when;
    const who = req.body.who;
    const what = req.body.what;

    MongoClient.connect(url, function (err, client) {
        if (err) throw err;
        //console.log("Connected successfully to server");
        const db = client.db(token);
        const collection = db.collection(when);
        // Insert some documents
        collection.updateOne({ num: who }, { $set: { num: who, include: what } }, { upsert: true }, function (err, result) {
            if (err) res.send("error");
            else {
                client.close();
                res.send("success");
            }
        });
    });
});
/*****************************************
 post:refresh => use token to find db, and use when to get goal, finally,return it
  *****************************************/
//date + "\n一連訓員 第2班\n今日看診人員:共0員\n發燒人員:共0員\n應到:16員 \n實到:" + result.length + "員" + str
router.post("/refresh", function (req, res) {
    const token = req.body.token;//each class have it's own db to save data,db's name is it's token
    const when = req.body.when;
    //console.log(req.body);
    MongoClient.connect(url, function (err, client) {
        if (err) throw "error";
        getUsers(token, client)
            .then(pkg => getResponse(pkg, token, when, client))
            .then(re => res.send(re))
            .catch(error => res.send(error));
    });
});

function getUsers(token, client) {
    return new Promise((resolve, reject) => {
        var table = client.db(dbUsers).collection(token);
        table.find({}).toArray(function (err, result) {
            err ? reject({ result: "connect error" }) : resolve(result);
        })
    });
}

function getResponse(pkg,token,when,client) {
    return new Promise((resolve, reject) => {
        var table = client.db(token).collection(when);
        table.find({}).toArray(function (err, result) {
            if (err)
                reject("connect error");
            else {
                var json = {};
                for (var i in result) json[result[i].num] = result[i].include;
                var str = "";
                for (var i in pkg)
                    str += "\n" + pkg[i].include + " : " + (json[pkg[i].num] != null ? json[pkg[i].num] : '<strong style="background-color: gray;">尚未回覆</strong>');
                //console.log(reply(token, when, result.length, str));
                //console.log("reply");
                resolve(reply(token.split('~'), when, result.length, str).replace());
            }
        })
    });
}

function reply(token,when, length, str) {
    return (
        when +
        "\n" + decodeURI(token[1]).toString() + "連訓員 第" + token[2] + "班\n今日看診人員:共0員\n發燒人員:共0員\n應到:" + token[3] + "員 \n實到:" +
        length +
        "員" +
        str
    );
}

module.exports = router;

前端

因為較為簡單不進行一個一個的說明僅對重點做敘述

設定頁(set.html)

禁止網頁放大

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

encodeURI,decodeURI

$.post("/buildClass", { name: encodeURI($("#newClassToken").val()), data: getData() }, function (result) {
由於'連'要支援中文,但之後藉由動態路由會出現再網址,要避免錯誤,於是利用encodeURI來進行轉換,於後端(index.js)最下面的reply函數中有decodeURI把其在輸出時轉換回中文。

輸入server網域

在最下方的函數,進行頁面跳轉,要加入伺服器的網域名

源碼
<!DOCTYPE html>
<html>

<head>
    <title>放假回報</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</head>

<body>
    <header>
        <div name="Title" class="jumbotron  mb-0 ">
            <div class="text-center  align-self-center">
                <h1>放假回報</h1>
            </div>
        </div>
    </header>
    <div class="container" style="font-family:Microsoft JhengHei;font-size:100%">
        <div id="input" style="text-align:center">
            <label>請輸入你的班級代號</label>
            <input id="classToken" type="text" />
            <input id="jumpPage" type="button" value="進入班級回報版" />
            <br>
            <hr />
            <label>新增班級回報版</label>
            <br>
            <p>班級代號規則 -> ex:3~步一~10~17~11~14~19 => (營)~(連)~(班)~(人數)~(第一時間)~(第二時間)~(第三時間)。</p>
            <input type="text" id="newClassToken" placeholder="請輸入班級代號"><br>
            <label>請按照順序輸入班級內所有成員的座號(左)及資訊(右)</label><br>
            <div style="display:none">01.<input id="n1" type="text" placeholder="ex:1"/><input type="text" id="s1" placeholder="ex:31001 王大明"><br></div>
            <div style="display:none">02.<input id="n2" type="text" placeholder="ex:2" /><input type="text" id="s2" placeholder="ex:31002 王大明"><br></div>
            <div style="display:none">03.<input id="n3" type="text" placeholder="ex:3" /><input type="text" id="s3" placeholder="ex:31003 王大明"><br></div>
            <div style="display:none">04.<input id="n4" type="text" placeholder="ex:4" /><input type="text" id="s4" placeholder="ex:31004 王大明"><br></div>
            <div style="display:none">05.<input id="n5" type="text" placeholder="ex:5" /><input type="text" id="s5" placeholder="ex:31005 王大明"><br></div>
            <div style="display:none">06.<input id="n6" type="text" placeholder="ex:6" /><input type="text" id="s6" placeholder="ex:31006 王大明"><br></div>
            <div style="display:none"> 07.<input id="n7" type="text" placeholder="ex:7" /><input type="text" id="s7" placeholder="ex:31007 王大明"><br></div>
            <div style="display:none">08.<input id="n8" type="text" placeholder="ex:8" /><input type="text" id="s8" placeholder="ex:31008 王大明"><br></div>
            <div style="display:none">09.<input id="n9" type="text" placeholder="ex:9" /><input type="text" id="s9" placeholder="ex:31009 王大明"><br></div>
            <div style="display:none"> 10.<input id="n10" type="text" placeholder="ex:10" /><input type="text" id="s10" placeholder="ex:31010 王大明"><br></div>
            <div style="display:none">11.<input id="n11" type="text" placeholder="ex:11" /><input type="text" id="s11" placeholder="ex:31011 王大明"><br></div>
            <div style="display:none">12.<input id="n12" type="text" placeholder="ex:12" /><input type="text" id="s12" placeholder="ex:31012 王大明"><br></div>
            <div style="display:none">13.<input id="n13" type="text" placeholder="ex:13" /><input type="text" id="s13" placeholder="ex:31013 王大明"><br></div>
            <div style="display:none">14.<input id="n14" type="text" placeholder="ex:14" /><input type="text" id="s14" placeholder="ex:31014 王大明"><br></div>
            <div style="display:none">15.<input id="n15" type="text" placeholder="ex:15" /><input type="text" id="s15" placeholder="ex:31015 王大明"><br></div>
            <div style="display:none">16.<input id="n16" type="text" placeholder="ex:16" /><input type="text" id="s16" placeholder="ex:31016 王大明"><br></div>
            <div style="display:none">17.<input id="n17" type="text" placeholder="ex:17" /><input type="text" id="s17" placeholder="ex:31017 王大明"><br></div>
            <div style="display:none">18.<input id="n18" type="text" placeholder="ex:18" /><input type="text" id="s18" placeholder="ex:31018 王大明"><br></div>
            <div style="display:none">19.<input id="n19" type="text" placeholder="ex:19" /><input type="text" id="s19" placeholder="ex:31019 王大明"><br></div>
            <div style="display:none">20.<input id="n20" type="text" placeholder="ex:20" /><input type="text" id="s20" placeholder="ex:31020 王大明"><br></div>
            <br>
            <input id="PushUser" type="button" value="增加成員" />
            <input id="PopUser" type="button" value="減少成員" />
            <br /><br />
            <button id="buildClass" class="btn btn-success">創建班級</button>
        </div>
    </div>
    <br>
    <br>
    <script>
        var count = 16;
        function getData() {
            var str = $("#n1").val() + "_" + $("#s1").val();
            for (var i = 2; i <= count; i++)
                str += "-" + $("#n" + i).val() + "_" + $("#s" + i).val();
            return str;
        }
        $(document).ready(function () {
            for (var i = count; i >= 1; i--)
                $("#s" + i).parent("div").show();
            $("#PushUser").click(function () {
                if (count < 20)
                    $("#s" + (++count)).parent("div").show();
                else
                    alert("20人為班級人數的極限");
            });
            $("#PopUser").click(function () {
                if (count > 2)
                    $("#s" + (count--)).parent("div").hide();
                else
                    alert("2人為班級人數的最小值");
            });
            $("#buildClass").click(function () {
                if ($("#newClassToken").val() != null) {
                    $.post("/buildClass", { name: encodeURI($("#newClassToken").val()), data: getData() }, function (result) {
                        alert(result);
                    })
                } else
                    alert("請填入課程代號");
            });
            $("#jumpPage").click(function () {
                location.href =  "這裡輸入伺服器網域名" +"/index/" + $("#classToken").val();
                //location.href = "http://127.0.0.1:1337/index/" + encodeURI($("#classToken").val());
            });
        });
    </script>
</body>

</html>

班級頁(index.html)

複製按鈕
function Copy(str) {
                //創建一個textarea標籤,由於該網頁API操作僅可對此標籤進行
                var clip_area = document.createElement('textarea');
                //把內容放入標籤
                clip_area.textContent = str;
                //新增標籤至實際網頁
                document.body.appendChild(clip_area);
                //選取該標籤
                clip_area.select();
                //執行複製指令
                document.execCommand('copy');
                //移除標籤
                clip_area.remove();
                alert("已複製好,可黏貼");
            }
源碼
<!DOCTYPE html>
<html>

<head>
    <title>放假回報</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</head>

<body>
    <header>
        <div name="Title" class="jumbotron  mb-0 ">
            <div class="text-center  align-self-center">
                <h1>放假回報</h1>
            </div>
        </div>
    </header>
    <div class="container" style="font-family:Microsoft JhengHei;font-size:100%">
        <div id="input" style="text-align:center">
            <label>請輸入你的學號末兩碼</label>
            <input type="number" id="number">
            <br>
            <label>回報時間</label>
            <select id="select">
                <option id="option1" value=""></option>
                <option id="option2" value=""></option>
                <option id="option3" value=""></option>
            </select>
            <br>
            <input type="text" id="text" placeholder="請輸入回報內容">
            <button id="send">傳送</button>
            <pre id="include"></pre>
            <button id="duplicate">複製</button>
            <button id="refresh">刷新</button>
        </div>
    </div>
    <script>
        $(document).ready(function () {
            var token = encodeURI('<%=token%>');
            //console.log(token);
            var dt = new Date();
            var now = dt.getHours();
            var select = document.getElementById("select");
            const times = token.split('~');
            $("#option1").val(times[4] + "時回報");
            $("#option2").val(times[5] + "時回報");
            $("#option3").val(times[6] + "時回報");
            $("#option1").html(times[4] + "時回報");
            $("#option2").html(times[5] + "時回報");
            $("#option3").html(times[6] + "時回報");
            if (now <= parseInt(times[4]) + 1)
                select.options[0].selected = true;
            else if (now <= parseInt(times[5]) + 1)
                select.options[1].selected = true;
            else
                select.options[2].selected = true;
            if (localStorage.getItem("num"))
                $("#number").val(localStorage.getItem("num"));
            $.post("/refresh", { token: token, when: (parseInt(dt.getFullYear()) - 1911).toString() + "/" + (parseInt(dt.getMonth()) + 1).toString() + "/" + dt.getDate().toString() + " " + $("select").val() }, function (result) {
                $("pre").html(result);
            })
            $("#send").click(function () {
                if (parseInt($("#number").val()) >= 0) {
                    $.post("/send", { token: token, when: (parseInt(dt.getFullYear()) - 1911).toString() + "/" + (parseInt(dt.getMonth()) + 1).toString() + "/" + dt.getDate().toString() + " " + $("select").val(), who: parseInt($("#number").val()).toString(), what: $("#text").val() }, function (result) {
                        $.post("/refresh", { token: token, when: (parseInt(dt.getFullYear()) - 1911).toString() + "/" + (parseInt(dt.getMonth()) + 1).toString() + "/" + dt.getDate().toString() + " " + $("select").val() }, function (result) {
                            $("pre").html(result);
                            $("#text").val('');
                        })
                    })
                    localStorage.setItem("num", $("#number").val().toString());
                } else {
                    alert('請檢查你的學號是否輸入正確');
                }
            });
            $("#refresh").click(function () {
                $.post("/refresh", { token: token, when: (parseInt(dt.getFullYear()) - 1911).toString() + "/" + (parseInt(dt.getMonth()) + 1).toString() + "/" + dt.getDate().toString() + " " + $("select").val() }, function (result) {
                    $("pre").html(result);
                })
            });
            function Copy(str) {
                var clip_area = document.createElement('textarea');
                clip_area.textContent = str;
                document.body.appendChild(clip_area);
                clip_area.select();
                document.execCommand('copy');
                clip_area.remove();
                alert("已複製好,可黏貼");
            }
            $("#duplicate").click(function () {
                Copy($("pre").html().replace(/<[^>]+>/g, ""));
            })
            $("select").change(function () {
                $.post("/refresh", { token: token, when: (parseInt(dt.getFullYear()) - 1911).toString() + "/" + (parseInt(dt.getMonth()) + 1).toString() + "/" + dt.getDate().toString() + " " + $("select").val() }, function (result) {
                    $("pre").html(result);
                })
            });
        })
    </script>
</body>

</html>

建構

若要實際發布此網站,在引用架構,編寫所有檔案後還不夠還有資料庫與伺服器的問題,不過由於不是本篇重點,所以我將簡單帶過。

資料庫

資料庫我是使用MongoDB Altis,帳號申辦簡單,以本專案來說也有相當夠用的免費存儲空間。

伺服器

我個人用過GCP(google clooud platform)在剛使用有一定額度的免費,但個人經驗一下就用完了,而且使用複雜度相對較高。
其他就是各種雲伺服器。
不過以我個人而言最喜歡使用的方案是安裝在個人可連接外網的機器裡,用pm2發布。

後話

git連結:
https://github.com/leon123858/soldiers_response_system/tree/main/Web/Web
若想直接使用,記得在index.js加入資料庫連結網址,在set.html加入伺服器網域才可以順利運作,在上方內容源碼區都有用中文補在該插入的地方。
此外
若講述不好歡迎建議或補充,
若講述有誤歡迎指正。


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言