@ -0,0 +1,3 @@ | |||
# Windows: | |||
__pycahce__ | |||
.vscode |
@ -0,0 +1,220 @@ | |||
from flask import Flask,render_template,request,abort,jsonify | |||
import json | |||
import os | |||
from xml.etree.ElementTree import Element,SubElement, tostring,ElementTree | |||
import datetime | |||
import functools | |||
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer | |||
import csv | |||
import hashlib | |||
HOME_PATH=os.getcwd() | |||
BIN_PATH=os.path.join(HOME_PATH,"bin") | |||
HOMEWORK_METADATA_PATH=os.path.join(HOME_PATH,"homeworkMetadata.xml") | |||
ADMININFO_PATH=os.path.join(HOME_PATH,"adminInfo.xml") | |||
STUDENTLIST_PATH=os.path.join(HOME_PATH,"studentList.csv") | |||
SECRET_KEY="abcdefghijk" | |||
app = Flask(__name__) | |||
def create_token(api_user): | |||
#第一个参数是内部的私钥,这里写在共用的配置信息里了,如果只是测试可以写死 | |||
#第二个参数是有效期(秒) | |||
s = Serializer(SECRET_KEY,expires_in=3600) | |||
#接收用户id转换与编码 | |||
token = s.dumps({"id":api_user}) | |||
return str(token,encoding="utf-8") | |||
def login_required(view_func): | |||
@functools.wraps(view_func) | |||
def verify_token(*args,**kwargs): | |||
try: | |||
#在请求头上拿到token | |||
token = request.headers["token"] | |||
token = bytes(token,encoding="utf-8") | |||
except Exception: | |||
#没接收的到token,给前端抛出错误 | |||
#这里的code推荐写一个文件统一管理。这里为了看着直观就先写死了。 | |||
return jsonify(code = 4103,msg = '缺少参数token') | |||
s = Serializer(SECRET_KEY) | |||
try: | |||
s.loads(token) | |||
except Exception: | |||
return jsonify(code = 4101,msg = "登录已过期") | |||
return view_func(*args,**kwargs) | |||
return verify_token | |||
@app.route("/") | |||
def root(): | |||
homeworks=getAllHomeworks() | |||
''' | |||
for i in range(len(homeworks)): | |||
deadline=homeworks[i]["deadline"] | |||
deadline=datetime.datetime.strptime(deadline, '%Y-%m-%d %H:%M:%S') | |||
now=datetime.datetime.now() | |||
countdown=deadline-now | |||
#homeworks[i]["countdown"]=str(countdown) | |||
homeworks[i]["countdown"]="100000" | |||
''' | |||
return render_template('root.html',homeworks=homeworks) | |||
@app.route("/upload",methods=["POST"]) | |||
def upload(): | |||
homeworkName=request.form.get("homeworkName") | |||
if findHomework(homeworkName) is None: | |||
return error() | |||
name = request.form.get("name") | |||
num = request.form.get("num") | |||
uploadFile = request.files['file'] | |||
folderPath=os.path.join(BIN_PATH,homeworkName) | |||
currentDateTime=datetime.datetime.now() | |||
uploadFile.save(os.path.join(folderPath,num+"_"+name+"_"+currentDateTime.strftime('%H-%M-%S')+"."+uploadFile.filename.split(".")[-1])) | |||
return json.dumps({"result": 1}) | |||
@app.route("/contact") | |||
def contact(): | |||
return render_template("contact.html") | |||
@app.route("/admin/login") | |||
def adminLogin(): | |||
return render_template('adminLogin.html') | |||
@app.route("/admin/verify",methods=["POST"]) | |||
def adminVerify(): | |||
username = request.form.get("username") | |||
password = request.form.get("password") | |||
if password=="123456" and username=="admin": | |||
return json.dumps({"result":True,"token":str(create_token(username))}) | |||
else: | |||
return json.dumps({"result":False}) | |||
@app.route("/admin") | |||
@login_required | |||
def admin(): | |||
allHomeworks=getAllHomeworks() | |||
return render_template("admin.html",homeworks=allHomeworks) | |||
@app.route("/admin/<homework>") | |||
@login_required | |||
def homework(homework): | |||
currentInfo=getCurrentHomework(homework) | |||
return render_template("homework.html",data=currentInfo,homeworkName=homework) | |||
@app.route("/admin/studentList") | |||
@login_required | |||
def studentList(): | |||
studentList=getStudentList() | |||
return render_template('studentList.html',data=studentList) | |||
@app.route("/admin/studentListFile",methods=["POST"]) | |||
@login_required | |||
def studentListFile(): | |||
uploadFile = request.files['file'] | |||
uploadFile.save(STUDENTLIST_PATH) | |||
return json.dumps({"result": 1}) | |||
@app.route("/admin/publish") | |||
@login_required | |||
def publish(): | |||
return render_template('publish.html') | |||
@app.route("/admin/newHomework",methods=["POST"]) | |||
@login_required | |||
def newHomework(): | |||
name = request.form.get("name") | |||
describe = request.form.get("describe") | |||
deadline=request.form.get("deadline") | |||
addHomework(name,describe,deadline) | |||
return json.dumps({"result": 1}) | |||
@app.route("/admin/cancel/<homework>",methods=["POST"]) | |||
@login_required | |||
def cancelHomework(homework): | |||
deleteHomework(homework) | |||
return json.dumps({"result": 1}) | |||
def getCurrentHomework(homework): | |||
homeworkFolderPath=os.path.join(BIN_PATH,homework) | |||
if not os.path.exists(homeworkFolderPath): | |||
return error() | |||
#已经交 | |||
homeworkFolder=os.listdir(homeworkFolderPath) | |||
if len(homeworkFolder)>0: | |||
for i in range(len(homeworkFolder)): | |||
homeworkFolder[i]=homeworkFolder[i].strip(".zip").split("_")[1] | |||
#未交 | |||
studentList=getStudentList() | |||
result=[i+["yes"] if i[1] in homeworkFolder else i+["no"] for i in studentList ] | |||
return result | |||
def getStudentList(): | |||
with open(STUDENTLIST_PATH,encoding = 'utf-8-sig') as f: | |||
stuList=csv.reader(f) | |||
result=[i for i in stuList] | |||
return result | |||
def getAllHomeworks(): | |||
root=ElementTree().parse(HOMEWORK_METADATA_PATH) | |||
result=[] | |||
for homework in list(root): | |||
tempDict={"name":homework.find("name").text} | |||
tempDict["describe"]=homework.find("describe").text | |||
tempDict["deadline"]=homework.find("deadline").text | |||
result.append(tempDict) | |||
return result | |||
def getHomeworkInfo(homework): | |||
tempDict={"name":homework.getElementsByTagName("name")} | |||
tempDict["describe"]=homework.getElementsByTagName("describe") | |||
tempDict["deadline"]=homework.getElementsByTagName("deadline") | |||
return tempDict | |||
def findHomework(homeworkName): | |||
tree=ElementTree().parse(HOMEWORK_METADATA_PATH) | |||
homeworks=list(tree) | |||
for homework in homeworks: | |||
name=homework.find("name").text | |||
if homeworkName==name: | |||
return homework | |||
return None | |||
def addHomework(name,describe,deadline): | |||
tree=ElementTree().parse(HOMEWORK_METADATA_PATH) | |||
tmp = SubElement(tree, "homework") | |||
SubElement(tmp, "name").text = name | |||
SubElement(tmp, "describe").text = describe | |||
SubElement(tmp, "deadline").text = deadline | |||
tree=ElementTree(tree) | |||
tree.write(HOMEWORK_METADATA_PATH,"utf-8") | |||
homeworkPath=os.path.join(BIN_PATH,name) | |||
if not os.path.exists(homeworkPath): | |||
os.mkdir(homeworkPath) | |||
def deleteHomework(homeworkName): | |||
tree=ElementTree().parse(HOMEWORK_METADATA_PATH) | |||
homeworks=list(tree) | |||
for homework in homeworks: | |||
name=homework.find("name").text | |||
if homeworkName==name: | |||
tree.remove(homework) | |||
break | |||
tree=ElementTree(tree) | |||
tree.write(HOMEWORK_METADATA_PATH,"utf-8") | |||
def error(): | |||
return abort(404) | |||
if __name__ == '__main__': | |||
app.run(host="127.0.0.1") |
@ -0,0 +1 @@ | |||
<homeworks><homework><name>云计算第一次作业</name><describe>熟悉远程登录</describe><deadline>2021-02-01 05:05:05</deadline></homework><homework><name>云计算第二次作业</name><describe>docker使用</describe><deadline>2021-3-01 05:05:05</deadline></homework></homeworks> |
@ -0,0 +1,22 @@ | |||
/*! | |||
* Start Bootstrap - Modern Business (https://startbootstrap.com/template/modern-business) | |||
* Copyright 2013-2020 Start Bootstrap | |||
* Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-logomodern-business-nav/blob/master/LICENSE) | |||
*/ | |||
body { | |||
padding-top: 56px; | |||
} | |||
.carousel-item { | |||
height: 65vh; | |||
min-height: 300px; | |||
background: no-repeat center center scroll; | |||
-webkit-background-size: cover; | |||
-moz-background-size: cover; | |||
-o-background-size: cover; | |||
background-size: cover; | |||
} | |||
.portfolio-item { | |||
margin-bottom: 30px; | |||
} |
@ -0,0 +1,75 @@ | |||
$(function() { | |||
$("#contactForm input,#contactForm textarea").jqBootstrapValidation({ | |||
preventSubmit: true, | |||
submitError: function($form, event, errors) { | |||
// additional error messages or events | |||
}, | |||
submitSuccess: function($form, event) { | |||
event.preventDefault(); // prevent default submit behaviour | |||
// get values from FORM | |||
var name = $("input#name").val(); | |||
var email = $("input#email").val(); | |||
var phone = $("input#phone").val(); | |||
var message = $("textarea#message").val(); | |||
var firstName = name; // For Success/Failure Message | |||
// Check for white space in name for Success/Fail message | |||
if (firstName.indexOf(' ') >= 0) { | |||
firstName = name.split(' ').slice(0, -1).join(' '); | |||
} | |||
$this = $("#sendMessageButton"); | |||
$this.prop("disabled", true); // Disable submit button until AJAX call is complete to prevent duplicate messages | |||
$.ajax({ | |||
url: "././mail/contact_me.php", | |||
type: "POST", | |||
data: { | |||
name: name, | |||
phone: phone, | |||
email: email, | |||
message: message | |||
}, | |||
cache: false, | |||
success: function() { | |||
// Success message | |||
$('#success').html("<div class='alert alert-success'>"); | |||
$('#success > .alert-success').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×") | |||
.append("</button>"); | |||
$('#success > .alert-success') | |||
.append("<strong>Your message has been sent. </strong>"); | |||
$('#success > .alert-success') | |||
.append('</div>'); | |||
//clear all fields | |||
$('#contactForm').trigger("reset"); | |||
}, | |||
error: function() { | |||
// Fail message | |||
$('#success').html("<div class='alert alert-danger'>"); | |||
$('#success > .alert-danger').html("<button type='button' class='close' data-dismiss='alert' aria-hidden='true'>×") | |||
.append("</button>"); | |||
$('#success > .alert-danger').append($("<strong>").text("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!")); | |||
$('#success > .alert-danger').append('</div>'); | |||
//clear all fields | |||
$('#contactForm').trigger("reset"); | |||
}, | |||
complete: function() { | |||
setTimeout(function() { | |||
$this.prop("disabled", false); // Re-enable submit button when AJAX call is complete | |||
}, 1000); | |||
} | |||
}); | |||
}, | |||
filter: function() { | |||
return $(this).is(":visible"); | |||
}, | |||
}); | |||
$("a[data-toggle=\"tab\"]").click(function(e) { | |||
e.preventDefault(); | |||
$(this).tab("show"); | |||
}); | |||
}); | |||
/*When clicking on Full hide fail/success boxes */ | |||
$('#name').focus(function() { | |||
$('#success').html(''); | |||
}); |
@ -0,0 +1,912 @@ | |||
/* jqBootstrapValidation | |||
* A plugin for automating validation on Twitter Bootstrap formatted forms. | |||
* | |||
* v1.3.6 | |||
* | |||
* License: MIT <http://opensource.org/licenses/mit-license.php> - see LICENSE file | |||
* | |||
* http://ReactiveRaven.github.com/jqBootstrapValidation/ | |||
*/ | |||
(function( $ ){ | |||
var createdElements = []; | |||
var defaults = { | |||
options: { | |||
prependExistingHelpBlock: false, | |||
sniffHtml: true, // sniff for 'required', 'maxlength', etc | |||
preventSubmit: true, // stop the form submit event from firing if validation fails | |||
submitError: false, // function called if there is an error when trying to submit | |||
submitSuccess: false, // function called just before a successful submit event is sent to the server | |||
semanticallyStrict: false, // set to true to tidy up generated HTML output | |||
autoAdd: { | |||
helpBlocks: true | |||
}, | |||
filter: function () { | |||
// return $(this).is(":visible"); // only validate elements you can see | |||
return true; // validate everything | |||
} | |||
}, | |||
methods: { | |||
init : function( options ) { | |||
var settings = $.extend(true, {}, defaults); | |||
settings.options = $.extend(true, settings.options, options); | |||
var $siblingElements = this; | |||
var uniqueForms = $.unique( | |||
$siblingElements.map( function () { | |||
return $(this).parents("form")[0]; | |||
}).toArray() | |||
); | |||
$(uniqueForms).bind("submit", function (e) { | |||
var $form = $(this); | |||
var warningsFound = 0; | |||
var $inputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter); | |||
$inputs.trigger("submit.validation").trigger("validationLostFocus.validation"); | |||
$inputs.each(function (i, el) { | |||
var $this = $(el), | |||
$controlGroup = $this.parents(".control-group").first(); | |||
if ( | |||
$controlGroup.hasClass("has-warning") | |||
) { | |||
$controlGroup.removeClass("has-warning").addClass("has-error"); | |||
warningsFound++; | |||
} | |||
}); | |||
$inputs.trigger("validationLostFocus.validation"); | |||
if (warningsFound) { | |||
if (settings.options.preventSubmit) { | |||
e.preventDefault(); | |||
} | |||
$form.addClass("has-error"); | |||
if ($.isFunction(settings.options.submitError)) { | |||
settings.options.submitError($form, e, $inputs.jqBootstrapValidation("collectErrors", true)); | |||
} | |||
} else { | |||
$form.removeClass("has-error"); | |||
if ($.isFunction(settings.options.submitSuccess)) { | |||
settings.options.submitSuccess($form, e); | |||
} | |||
} | |||
}); | |||
return this.each(function(){ | |||
// Get references to everything we're interested in | |||
var $this = $(this), | |||
$controlGroup = $this.parents(".control-group").first(), | |||
$helpBlock = $controlGroup.find(".help-block").first(), | |||
$form = $this.parents("form").first(), | |||
validatorNames = []; | |||
// create message container if not exists | |||
if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) { | |||
$helpBlock = $('<div class="help-block" />'); | |||
$controlGroup.find('.controls').append($helpBlock); | |||
createdElements.push($helpBlock[0]); | |||
} | |||
// ============================================================= | |||
// SNIFF HTML FOR VALIDATORS | |||
// ============================================================= | |||
// *snort sniff snuffle* | |||
if (settings.options.sniffHtml) { | |||
var message = ""; | |||
// --------------------------------------------------------- | |||
// PATTERN | |||
// --------------------------------------------------------- | |||
if ($this.attr("pattern") !== undefined) { | |||
message = "Not in the expected format<!-- data-validation-pattern-message to override -->"; | |||
if ($this.data("validationPatternMessage")) { | |||
message = $this.data("validationPatternMessage"); | |||
} | |||
$this.data("validationPatternMessage", message); | |||
$this.data("validationPatternRegex", $this.attr("pattern")); | |||
} | |||
// --------------------------------------------------------- | |||
// MAX | |||
// --------------------------------------------------------- | |||
if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) { | |||
var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax")); | |||
message = "Too high: Maximum of '" + max + "'<!-- data-validation-max-message to override -->"; | |||
if ($this.data("validationMaxMessage")) { | |||
message = $this.data("validationMaxMessage"); | |||
} | |||
$this.data("validationMaxMessage", message); | |||
$this.data("validationMaxMax", max); | |||
} | |||
// --------------------------------------------------------- | |||
// MIN | |||
// --------------------------------------------------------- | |||
if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) { | |||
var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin")); | |||
message = "Too low: Minimum of '" + min + "'<!-- data-validation-min-message to override -->"; | |||
if ($this.data("validationMinMessage")) { | |||
message = $this.data("validationMinMessage"); | |||
} | |||
$this.data("validationMinMessage", message); | |||
$this.data("validationMinMin", min); | |||
} | |||
// --------------------------------------------------------- | |||
// MAXLENGTH | |||
// --------------------------------------------------------- | |||
if ($this.attr("maxlength") !== undefined) { | |||
message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters<!-- data-validation-maxlength-message to override -->"; | |||
if ($this.data("validationMaxlengthMessage")) { | |||
message = $this.data("validationMaxlengthMessage"); | |||
} | |||
$this.data("validationMaxlengthMessage", message); | |||
$this.data("validationMaxlengthMaxlength", $this.attr("maxlength")); | |||
} | |||
// --------------------------------------------------------- | |||
// MINLENGTH | |||
// --------------------------------------------------------- | |||
if ($this.attr("minlength") !== undefined) { | |||
message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters<!-- data-validation-minlength-message to override -->"; | |||
if ($this.data("validationMinlengthMessage")) { | |||
message = $this.data("validationMinlengthMessage"); | |||
} | |||
$this.data("validationMinlengthMessage", message); | |||
$this.data("validationMinlengthMinlength", $this.attr("minlength")); | |||
} | |||
// --------------------------------------------------------- | |||
// REQUIRED | |||
// --------------------------------------------------------- | |||
if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) { | |||
message = settings.builtInValidators.required.message; | |||
if ($this.data("validationRequiredMessage")) { | |||
message = $this.data("validationRequiredMessage"); | |||
} | |||
$this.data("validationRequiredMessage", message); | |||
} | |||
// --------------------------------------------------------- | |||
// NUMBER | |||
// --------------------------------------------------------- | |||
if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") { | |||
message = settings.builtInValidators.number.message; | |||
if ($this.data("validationNumberMessage")) { | |||
message = $this.data("validationNumberMessage"); | |||
} | |||
$this.data("validationNumberMessage", message); | |||
} | |||
// --------------------------------------------------------- | |||
// --------------------------------------------------------- | |||
if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") { | |||
message = "Not a valid email address<!-- data-validator-validemail-message to override -->"; | |||
if ($this.data("validationValidemailMessage")) { | |||
message = $this.data("validationValidemailMessage"); | |||
} else if ($this.data("validationEmailMessage")) { | |||
message = $this.data("validationEmailMessage"); | |||
} | |||
$this.data("validationValidemailMessage", message); | |||
} | |||
// --------------------------------------------------------- | |||
// MINCHECKED | |||
// --------------------------------------------------------- | |||
if ($this.attr("minchecked") !== undefined) { | |||
message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required<!-- data-validation-minchecked-message to override -->"; | |||
if ($this.data("validationMincheckedMessage")) { | |||
message = $this.data("validationMincheckedMessage"); | |||
} | |||
$this.data("validationMincheckedMessage", message); | |||
$this.data("validationMincheckedMinchecked", $this.attr("minchecked")); | |||
} | |||
// --------------------------------------------------------- | |||
// MAXCHECKED | |||
// --------------------------------------------------------- | |||
if ($this.attr("maxchecked") !== undefined) { | |||
message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required<!-- data-validation-maxchecked-message to override -->"; | |||
if ($this.data("validationMaxcheckedMessage")) { | |||
message = $this.data("validationMaxcheckedMessage"); | |||
} | |||
$this.data("validationMaxcheckedMessage", message); | |||
$this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked")); | |||
} | |||
} | |||
// ============================================================= | |||
// COLLECT VALIDATOR NAMES | |||
// ============================================================= | |||
// Get named validators | |||
if ($this.data("validation") !== undefined) { | |||
validatorNames = $this.data("validation").split(","); | |||
} | |||
// Get extra ones defined on the element's data attributes | |||
$.each($this.data(), function (i, el) { | |||
var parts = i.replace(/([A-Z])/g, ",$1").split(","); | |||
if (parts[0] === "validation" && parts[1]) { | |||
validatorNames.push(parts[1]); | |||
} | |||
}); | |||
// ============================================================= | |||
// NORMALISE VALIDATOR NAMES | |||
// ============================================================= | |||
var validatorNamesToInspect = validatorNames; | |||
var newValidatorNamesToInspect = []; | |||
do // repeatedly expand 'shortcut' validators into their real validators | |||
{ | |||
// Uppercase only the first letter of each name | |||
$.each(validatorNames, function (i, el) { | |||
validatorNames[i] = formatValidatorName(el); | |||
}); | |||
// Remove duplicate validator names | |||
validatorNames = $.unique(validatorNames); | |||
// Pull out the new validator names from each shortcut | |||
newValidatorNamesToInspect = []; | |||
$.each(validatorNamesToInspect, function(i, el) { | |||
if ($this.data("validation" + el + "Shortcut") !== undefined) { | |||
// Are these custom validators? | |||
// Pull them out! | |||
$.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) { | |||
newValidatorNamesToInspect.push(el2); | |||
}); | |||
} else if (settings.builtInValidators[el.toLowerCase()]) { | |||
// Is this a recognised built-in? | |||
// Pull it out! | |||
var validator = settings.builtInValidators[el.toLowerCase()]; | |||
if (validator.type.toLowerCase() === "shortcut") { | |||
$.each(validator.shortcut.split(","), function (i, el) { | |||
el = formatValidatorName(el); | |||
newValidatorNamesToInspect.push(el); | |||
validatorNames.push(el); | |||
}); | |||
} | |||
} | |||
}); | |||
validatorNamesToInspect = newValidatorNamesToInspect; | |||
} while (validatorNamesToInspect.length > 0) | |||
// ============================================================= | |||
// SET UP VALIDATOR ARRAYS | |||
// ============================================================= | |||
var validators = {}; | |||
$.each(validatorNames, function (i, el) { | |||
// Set up the 'override' message | |||
var message = $this.data("validation" + el + "Message"); | |||
var hasOverrideMessage = (message !== undefined); | |||
var foundValidator = false; | |||
message = | |||
( | |||
message | |||
? message | |||
: "'" + el + "' validation failed <!-- Add attribute 'data-validation-" + el.toLowerCase() + "-message' to input to change this message -->" | |||
) | |||
; | |||
$.each( | |||
settings.validatorTypes, | |||
function (validatorType, validatorTemplate) { | |||
if (validators[validatorType] === undefined) { | |||
validators[validatorType] = []; | |||
} | |||
if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) { | |||
validators[validatorType].push( | |||
$.extend( | |||
true, | |||
{ | |||
name: formatValidatorName(validatorTemplate.name), | |||
message: message | |||
}, | |||
validatorTemplate.init($this, el) | |||
) | |||
); | |||
foundValidator = true; | |||
} | |||
} | |||
); | |||
if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) { | |||
var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]); | |||
if (hasOverrideMessage) { | |||
validator.message = message; | |||
} | |||
var validatorType = validator.type.toLowerCase(); | |||
if (validatorType === "shortcut") { | |||
foundValidator = true; | |||
} else { | |||
$.each( | |||
settings.validatorTypes, | |||
function (validatorTemplateType, validatorTemplate) { | |||
if (validators[validatorTemplateType] === undefined) { | |||
validators[validatorTemplateType] = []; | |||
} | |||
if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) { | |||
$this.data("validation" + el + formatValidatorName(validatorTemplate.name), validator[validatorTemplate.name.toLowerCase()]); | |||
validators[validatorType].push( | |||
$.extend( | |||
validator, | |||
validatorTemplate.init($this, el) | |||
) | |||
); | |||
foundValidator = true; | |||
} | |||
} | |||
); | |||
} | |||
} | |||
if (! foundValidator) { | |||
$.error("Cannot find validation info for '" + el + "'"); | |||
} | |||
}); | |||
// ============================================================= | |||
// STORE FALLBACK VALUES | |||
// ============================================================= | |||
$helpBlock.data( | |||
"original-contents", | |||
( | |||
$helpBlock.data("original-contents") | |||
? $helpBlock.data("original-contents") | |||
: $helpBlock.html() | |||
) | |||
); | |||
$helpBlock.data( | |||
"original-role", | |||
( | |||
$helpBlock.data("original-role") | |||
? $helpBlock.data("original-role") | |||
: $helpBlock.attr("role") | |||
) | |||
); | |||
$controlGroup.data( | |||
"original-classes", | |||
( | |||
$controlGroup.data("original-clases") | |||
? $controlGroup.data("original-classes") | |||
: $controlGroup.attr("class") | |||
) | |||
); | |||
$this.data( | |||
"original-aria-invalid", | |||
( | |||
$this.data("original-aria-invalid") | |||
? $this.data("original-aria-invalid") | |||
: $this.attr("aria-invalid") | |||
) | |||
); | |||
// ============================================================= | |||
// VALIDATION | |||
// ============================================================= | |||
$this.bind( | |||
"validation.validation", | |||
function (event, params) { | |||
var value = getValue($this); | |||
// Get a list of the errors to apply | |||
var errorsFound = []; | |||
$.each(validators, function (validatorType, validatorTypeArray) { | |||
if (value || value.length || (params && params.includeEmpty) || (!!settings.validatorTypes[validatorType].blockSubmit && params && !!params.submitting)) { | |||
$.each(validatorTypeArray, function (i, validator) { | |||
if (settings.validatorTypes[validatorType].validate($this, value, validator)) { | |||
errorsFound.push(validator.message); | |||
} | |||
}); | |||
} | |||
}); | |||
return errorsFound; | |||
} | |||
); | |||
$this.bind( | |||
"getValidators.validation", | |||
function () { | |||
return validators; | |||
} | |||
); | |||
// ============================================================= | |||
// WATCH FOR CHANGES | |||
// ============================================================= | |||
$this.bind( | |||
"submit.validation", | |||
function () { | |||
return $this.triggerHandler("change.validation", {submitting: true}); | |||
} | |||
); | |||
$this.bind( | |||
[ | |||
"keyup", | |||
"focus", | |||
"blur", | |||
"click", | |||
"keydown", | |||
"keypress", | |||
"change" | |||
].join(".validation ") + ".validation", | |||
function (e, params) { | |||
var value = getValue($this); | |||
var errorsFound = []; | |||
$controlGroup.find("input,textarea,select").each(function (i, el) { | |||
var oldCount = errorsFound.length; | |||
$.each($(el).triggerHandler("validation.validation", params), function (j, message) { | |||
errorsFound.push(message); | |||
}); | |||
if (errorsFound.length > oldCount) { | |||
$(el).attr("aria-invalid", "true"); | |||
} else { | |||
var original = $this.data("original-aria-invalid"); | |||
$(el).attr("aria-invalid", (original !== undefined ? original : false)); | |||
} | |||
}); | |||
$form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation"); | |||
errorsFound = $.unique(errorsFound.sort()); | |||
// Were there any errors? | |||
if (errorsFound.length) { | |||
// Better flag it up as a warning. | |||
$controlGroup.removeClass("has-success has-error").addClass("has-warning"); | |||
// How many errors did we find? | |||
if (settings.options.semanticallyStrict && errorsFound.length === 1) { | |||
// Only one? Being strict? Just output it. | |||
$helpBlock.html(errorsFound[0] + | |||
( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); | |||
} else { | |||
// Multiple? Being sloppy? Glue them together into an UL. | |||
$helpBlock.html("<ul role=\"alert\"><li>" + errorsFound.join("</li><li>") + "</li></ul>" + | |||
( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); | |||
} | |||
} else { | |||
$controlGroup.removeClass("has-warning has-error has-success"); | |||
if (value.length > 0) { | |||
$controlGroup.addClass("has-success"); | |||
} | |||
$helpBlock.html($helpBlock.data("original-contents")); | |||
} | |||
if (e.type === "blur") { | |||
$controlGroup.removeClass("has-success"); | |||
} | |||
} | |||
); | |||
$this.bind("validationLostFocus.validation", function () { | |||
$controlGroup.removeClass("has-success"); | |||
}); | |||
}); | |||
}, | |||
destroy : function( ) { | |||
return this.each( | |||
function() { | |||
var | |||
$this = $(this), | |||
$controlGroup = $this.parents(".control-group").first(), | |||
$helpBlock = $controlGroup.find(".help-block").first(); | |||
// remove our events | |||
$this.unbind('.validation'); // events are namespaced. | |||
// reset help text | |||
$helpBlock.html($helpBlock.data("original-contents")); | |||
// reset classes | |||
$controlGroup.attr("class", $controlGroup.data("original-classes")); | |||
// reset aria | |||
$this.attr("aria-invalid", $this.data("original-aria-invalid")); | |||
// reset role | |||
$helpBlock.attr("role", $this.data("original-role")); | |||
// remove all elements we created | |||
if (createdElements.indexOf($helpBlock[0]) > -1) { | |||
$helpBlock.remove(); | |||
} | |||
} | |||
); | |||
}, | |||
collectErrors : function(includeEmpty) { | |||
var errorMessages = {}; | |||
this.each(function (i, el) { | |||
var $el = $(el); | |||
var name = $el.attr("name"); | |||
var errors = $el.triggerHandler("validation.validation", {includeEmpty: true}); | |||
errorMessages[name] = $.extend(true, errors, errorMessages[name]); | |||
}); | |||
$.each(errorMessages, function (i, el) { | |||
if (el.length === 0) { | |||
delete errorMessages[i]; | |||
} | |||
}); | |||
return errorMessages; | |||
}, | |||
hasErrors: function() { | |||
var errorMessages = []; | |||
this.each(function (i, el) { | |||
errorMessages = errorMessages.concat( | |||
$(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : [] | |||
); | |||
}); | |||
return (errorMessages.length > 0); | |||
}, | |||
override : function (newDefaults) { | |||
defaults = $.extend(true, defaults, newDefaults); | |||
} | |||
}, | |||
validatorTypes: { | |||
callback: { | |||
name: "callback", | |||
init: function ($this, name) { | |||
return { | |||
validatorName: name, | |||
callback: $this.data("validation" + name + "Callback"), | |||
lastValue: $this.val(), | |||
lastValid: true, | |||
lastFinished: true | |||
}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
if (validator.lastValue === value && validator.lastFinished) { | |||
return !validator.lastValid; | |||
} | |||
if (validator.lastFinished === true) | |||
{ | |||
validator.lastValue = value; | |||
validator.lastValid = true; | |||
validator.lastFinished = false; | |||
var rrjqbvValidator = validator; | |||
var rrjqbvThis = $this; | |||
executeFunctionByName( | |||
validator.callback, | |||
window, | |||
$this, | |||
value, | |||
function (data) { | |||
if (rrjqbvValidator.lastValue === data.value) { | |||
rrjqbvValidator.lastValid = data.valid; | |||
if (data.message) { | |||
rrjqbvValidator.message = data.message; | |||
} | |||
rrjqbvValidator.lastFinished = true; | |||
rrjqbvThis.data("validation" + rrjqbvValidator.validatorName + "Message", rrjqbvValidator.message); | |||
// Timeout is set to avoid problems with the events being considered 'already fired' | |||
setTimeout(function () { | |||
rrjqbvThis.trigger("change.validation"); | |||
}, 1); // doesn't need a long timeout, just long enough for the event bubble to burst | |||
} | |||
} | |||
); | |||
} | |||
return false; | |||
} | |||
}, | |||
ajax: { | |||
name: "ajax", | |||
init: function ($this, name) { | |||
return { | |||
validatorName: name, | |||
url: $this.data("validation" + name + "Ajax"), | |||
lastValue: $this.val(), | |||
lastValid: true, | |||
lastFinished: true | |||
}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
if (""+validator.lastValue === ""+value && validator.lastFinished === true) { | |||
return validator.lastValid === false; | |||
} | |||
if (validator.lastFinished === true) | |||
{ | |||
validator.lastValue = value; | |||
validator.lastValid = true; | |||
validator.lastFinished = false; | |||
$.ajax({ | |||
url: validator.url, | |||
data: "value=" + value + "&field=" + $this.attr("name"), | |||
dataType: "json", | |||
success: function (data) { | |||
if (""+validator.lastValue === ""+data.value) { | |||
validator.lastValid = !!(data.valid); | |||
if (data.message) { | |||
validator.message = data.message; | |||
} | |||
validator.lastFinished = true; | |||
$this.data("validation" + validator.validatorName + "Message", validator.message); | |||
// Timeout is set to avoid problems with the events being considered 'already fired' | |||
setTimeout(function () { | |||
$this.trigger("change.validation"); | |||
}, 1); // doesn't need a long timeout, just long enough for the event bubble to burst | |||
} | |||
}, | |||
failure: function () { | |||
validator.lastValid = true; | |||
validator.message = "ajax call failed"; | |||
validator.lastFinished = true; | |||
$this.data("validation" + validator.validatorName + "Message", validator.message); | |||
// Timeout is set to avoid problems with the events being considered 'already fired' | |||
setTimeout(function () { | |||
$this.trigger("change.validation"); | |||
}, 1); // doesn't need a long timeout, just long enough for the event bubble to burst | |||
} | |||
}); | |||
} | |||
return false; | |||
} | |||
}, | |||
regex: { | |||
name: "regex", | |||
init: function ($this, name) { | |||
return {regex: regexFromString($this.data("validation" + name + "Regex"))}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return (!validator.regex.test(value) && ! validator.negative) | |||
|| (validator.regex.test(value) && validator.negative); | |||
} | |||
}, | |||
required: { | |||
name: "required", | |||
init: function ($this, name) { | |||
return {}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return !!(value.length === 0 && ! validator.negative) | |||
|| !!(value.length > 0 && validator.negative); | |||
}, | |||
blockSubmit: true | |||
}, | |||
match: { | |||
name: "match", | |||
init: function ($this, name) { | |||
var element = $this.parents("form").first().find("[name=\"" + $this.data("validation" + name + "Match") + "\"]").first(); | |||
element.bind("validation.validation", function () { | |||
$this.trigger("change.validation", {submitting: true}); | |||
}); | |||
return {"element": element}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return (value !== validator.element.val() && ! validator.negative) | |||
|| (value === validator.element.val() && validator.negative); | |||
}, | |||
blockSubmit: true | |||
}, | |||
max: { | |||
name: "max", | |||
init: function ($this, name) { | |||
return {max: $this.data("validation" + name + "Max")}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative) | |||
|| (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative); | |||
} | |||
}, | |||
min: { | |||
name: "min", | |||
init: function ($this, name) { | |||
return {min: $this.data("validation" + name + "Min")}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative) | |||
|| (parseFloat(value) >= parseFloat(validator.min) && validator.negative); | |||
} | |||
}, | |||
maxlength: { | |||
name: "maxlength", | |||
init: function ($this, name) { | |||
return {maxlength: $this.data("validation" + name + "Maxlength")}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return ((value.length > validator.maxlength) && ! validator.negative) | |||
|| ((value.length <= validator.maxlength) && validator.negative); | |||
} | |||
}, | |||
minlength: { | |||
name: "minlength", | |||
init: function ($this, name) { | |||
return {minlength: $this.data("validation" + name + "Minlength")}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return ((value.length < validator.minlength) && ! validator.negative) | |||
|| ((value.length >= validator.minlength) && validator.negative); | |||
} | |||
}, | |||
maxchecked: { | |||
name: "maxchecked", | |||
init: function ($this, name) { | |||
var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); | |||
elements.bind("click.validation", function () { | |||
$this.trigger("change.validation", {includeEmpty: true}); | |||
}); | |||
return {maxchecked: $this.data("validation" + name + "Maxchecked"), elements: elements}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative) | |||
|| (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative); | |||
}, | |||
blockSubmit: true | |||
}, | |||
minchecked: { | |||
name: "minchecked", | |||
init: function ($this, name) { | |||
var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); | |||
elements.bind("click.validation", function () { | |||
$this.trigger("change.validation", {includeEmpty: true}); | |||
}); | |||
return {minchecked: $this.data("validation" + name + "Minchecked"), elements: elements}; | |||
}, | |||
validate: function ($this, value, validator) { | |||
return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative) | |||
|| (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative); | |||
}, | |||
blockSubmit: true | |||
} | |||
}, | |||
builtInValidators: { | |||
email: { | |||
name: "Email", | |||
type: "shortcut", | |||
shortcut: "validemail" | |||
}, | |||
validemail: { | |||
name: "Validemail", | |||
type: "regex", | |||
regex: "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\.[A-Za-z]{2,4}", | |||
message: "Not a valid email address<!-- data-validator-validemail-message to override -->" | |||
}, | |||
passwordagain: { | |||
name: "Passwordagain", | |||
type: "match", | |||
match: "password", | |||
message: "Does not match the given password<!-- data-validator-paswordagain-message to override -->" | |||
}, | |||
positive: { | |||
name: "Positive", | |||
type: "shortcut", | |||
shortcut: "number,positivenumber" | |||
}, | |||
negative: { | |||
name: "Negative", | |||
type: "shortcut", | |||
shortcut: "number,negativenumber" | |||
}, | |||
number: { | |||
name: "Number", | |||
type: "regex", | |||
regex: "([+-]?\\\d+(\\\.\\\d*)?([eE][+-]?[0-9]+)?)?", | |||
message: "Must be a number<!-- data-validator-number-message to override -->" | |||
}, | |||
integer: { | |||
name: "Integer", | |||
type: "regex", | |||
regex: "[+-]?\\\d+", | |||
message: "No decimal places allowed<!-- data-validator-integer-message to override -->" | |||
}, | |||
positivenumber: { | |||
name: "Positivenumber", | |||
type: "min", | |||
min: 0, | |||
message: "Must be a positive number<!-- data-validator-positivenumber-message to override -->" | |||
}, | |||
negativenumber: { | |||
name: "Negativenumber", | |||
type: "max", | |||
max: 0, | |||
message: "Must be a negative number<!-- data-validator-negativenumber-message to override -->" | |||
}, | |||
required: { | |||
name: "Required", | |||
type: "required", | |||
message: "This is required<!-- data-validator-required-message to override -->" | |||
}, | |||
checkone: { | |||
name: "Checkone", | |||
type: "minchecked", | |||
minchecked: 1, | |||
message: "Check at least one option<!-- data-validation-checkone-message to override -->" | |||
} | |||
} | |||
}; | |||
var formatValidatorName = function (name) { | |||
return name | |||
.toLowerCase() | |||
.replace( | |||
/(^|\s)([a-z])/g , | |||
function(m,p1,p2) { | |||
return p1+p2.toUpperCase(); | |||
} | |||
) | |||
; | |||
}; | |||
var getValue = function ($this) { | |||
// Extract the value we're talking about | |||
var value = $this.val(); | |||
var type = $this.attr("type"); | |||
if (type === "checkbox") { | |||
value = ($this.is(":checked") ? value : ""); | |||
} | |||
if (type === "radio") { | |||
value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? value : ""); | |||
} | |||
return value; | |||
}; | |||
function regexFromString(inputstring) { | |||
return new RegExp("^" + inputstring + "$"); | |||
} | |||
/** | |||
* Thanks to Jason Bunting via StackOverflow.com | |||
* | |||
* http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string#answer-359910 | |||
* Short link: http://tinyurl.com/executeFunctionByName | |||
**/ | |||
function executeFunctionByName(functionName, context /*, args*/) { | |||
var args = Array.prototype.slice.call(arguments).splice(2); | |||
var namespaces = functionName.split("."); | |||
var func = namespaces.pop(); | |||
for(var i = 0; i < namespaces.length; i++) { | |||
context = context[namespaces[i]]; | |||
} | |||
return context[func].apply(this, args); | |||
} | |||
$.fn.jqBootstrapValidation = function( method ) { | |||
if ( defaults.methods[method] ) { | |||
return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); | |||
} else if ( typeof method === 'object' || ! method ) { | |||
return defaults.methods.init.apply( this, arguments ); | |||
} else { | |||
$.error( 'Method ' + method + ' does not exist on jQuery.jqBootstrapValidation' ); | |||
return null; | |||
} | |||
}; | |||
$.jqBootstrapValidation = function (options) { | |||
$(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments); | |||
}; | |||
})( jQuery ); |
@ -0,0 +1,326 @@ | |||
/*! | |||
* Bootstrap Reboot v4.5.3 (https://getbootstrap.com/) | |||
* Copyright 2011-2020 The Bootstrap Authors | |||
* Copyright 2011-2020 Twitter, Inc. | |||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) | |||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) | |||
*/ | |||
*, | |||
*::before, | |||
*::after { | |||
box-sizing: border-box; | |||
} | |||
html { | |||
font-family: sans-serif; | |||
line-height: 1.15; | |||
-webkit-text-size-adjust: 100%; | |||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0); | |||
} | |||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { | |||
display: block; | |||
} | |||
body { | |||
margin: 0; | |||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | |||
font-size: 1rem; | |||
font-weight: 400; | |||
line-height: 1.5; | |||
color: #212529; | |||
text-align: left; | |||
background-color: #fff; | |||
} | |||
[tabindex="-1"]:focus:not(:focus-visible) { | |||
outline: 0 !important; | |||
} | |||
hr { | |||
box-sizing: content-box; | |||
height: 0; | |||
overflow: visible; | |||
} | |||
h1, h2, h3, h4, h5, h6 { | |||
margin-top: 0; | |||
margin-bottom: 0.5rem; | |||
} | |||
p { | |||
margin-top: 0; | |||
margin-bottom: 1rem; | |||
} | |||
abbr[title], | |||
abbr[data-original-title] { | |||
text-decoration: underline; | |||
-webkit-text-decoration: underline dotted; | |||
text-decoration: underline dotted; | |||
cursor: help; | |||
border-bottom: 0; | |||
-webkit-text-decoration-skip-ink: none; | |||
text-decoration-skip-ink: none; | |||
} | |||
address { | |||
margin-bottom: 1rem; | |||
font-style: normal; | |||
line-height: inherit; | |||
} | |||
ol, | |||
ul, | |||
dl { | |||
margin-top: 0; | |||
margin-bottom: 1rem; | |||
} | |||
ol ol, | |||
ul ul, | |||
ol ul, | |||
ul ol { | |||
margin-bottom: 0; | |||
} | |||
dt { | |||
font-weight: 700; | |||
} | |||
dd { | |||
margin-bottom: .5rem; | |||
margin-left: 0; | |||
} | |||
blockquote { | |||
margin: 0 0 1rem; | |||
} | |||
b, | |||
strong { | |||
font-weight: bolder; | |||
} | |||
small { | |||
font-size: 80%; | |||
} | |||
sub, | |||
sup { | |||
position: relative; | |||
font-size: 75%; | |||
line-height: 0; | |||
vertical-align: baseline; | |||
} | |||
sub { | |||
bottom: -.25em; | |||
} | |||
sup { | |||
top: -.5em; | |||
} | |||
a { | |||
color: #007bff; | |||
text-decoration: none; | |||
background-color: transparent; | |||
} | |||
a:hover { | |||
color: #0056b3; | |||
text-decoration: underline; | |||
} | |||
a:not([href]):not([class]) { | |||
color: inherit; | |||
text-decoration: none; | |||
} | |||
a:not([href]):not([class]):hover { | |||
color: inherit; | |||
text-decoration: none; | |||
} | |||
pre, | |||
code, | |||
kbd, | |||
samp { | |||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; | |||
font-size: 1em; | |||
} | |||
pre { | |||
margin-top: 0; | |||
margin-bottom: 1rem; | |||
overflow: auto; | |||
-ms-overflow-style: scrollbar; | |||
} | |||
figure { | |||
margin: 0 0 1rem; | |||
} | |||
img { | |||
vertical-align: middle; | |||
border-style: none; | |||
} | |||
svg { | |||
overflow: hidden; | |||
vertical-align: middle; | |||
} | |||
table { | |||
border-collapse: collapse; | |||
} | |||
caption { | |||
padding-top: 0.75rem; | |||
padding-bottom: 0.75rem; | |||
color: #6c757d; | |||
text-align: left; | |||
caption-side: bottom; | |||
} | |||
th { | |||
text-align: inherit; | |||
text-align: -webkit-match-parent; | |||
} | |||
label { | |||
display: inline-block; | |||
margin-bottom: 0.5rem; | |||
} | |||
button { | |||
border-radius: 0; | |||
} | |||
button:focus { | |||
outline: 1px dotted; | |||
outline: 5px auto -webkit-focus-ring-color; | |||
} | |||
input, | |||
button, | |||
select, | |||
optgroup, | |||
textarea { | |||
margin: 0; | |||
font-family: inherit; | |||
font-size: inherit; | |||
line-height: inherit; | |||
} | |||
button, | |||
input { | |||
overflow: visible; | |||
} | |||
button, | |||
select { | |||
text-transform: none; | |||
} | |||
[role="button"] { | |||
cursor: pointer; | |||
} | |||
select { | |||
word-wrap: normal; | |||
} | |||
button, | |||
[type="button"], | |||
[type="reset"], | |||
[type="submit"] { | |||
-webkit-appearance: button; | |||
} | |||
button:not(:disabled), | |||
[type="button"]:not(:disabled), | |||
[type="reset"]:not(:disabled), | |||
[type="submit"]:not(:disabled) { | |||
cursor: pointer; | |||
} | |||
button::-moz-focus-inner, | |||
[type="button"]::-moz-focus-inner, | |||
[type="reset"]::-moz-focus-inner, | |||
[type="submit"]::-moz-focus-inner { | |||
padding: 0; | |||
border-style: none; | |||
} | |||
input[type="radio"], | |||
input[type="checkbox"] { | |||
box-sizing: border-box; | |||
padding: 0; | |||
} | |||
textarea { | |||
overflow: auto; | |||
resize: vertical; | |||
} | |||
fieldset { | |||
min-width: 0; | |||
padding: 0; | |||
margin: 0; | |||
border: 0; | |||
} | |||
legend { | |||
display: block; | |||
width: 100%; | |||
max-width: 100%; | |||
padding: 0; | |||
margin-bottom: .5rem; | |||
font-size: 1.5rem; | |||
line-height: inherit; | |||
color: inherit; | |||
white-space: normal; | |||
} | |||
progress { | |||
vertical-align: baseline; | |||
} | |||
[type="number"]::-webkit-inner-spin-button, | |||
[type="number"]::-webkit-outer-spin-button { | |||
height: auto; | |||
} | |||
[type="search"] { | |||
outline-offset: -2px; | |||
-webkit-appearance: none; | |||
} | |||
[type="search"]::-webkit-search-decoration { | |||
-webkit-appearance: none; | |||
} | |||
::-webkit-file-upload-button { | |||
font: inherit; | |||
-webkit-appearance: button; | |||
} | |||
output { | |||
display: inline-block; | |||
} | |||
summary { | |||
display: list-item; | |||
cursor: pointer; | |||
} | |||
template { | |||
display: none; | |||
} | |||
[hidden] { | |||
display: none !important; | |||
} | |||
/*# sourceMappingURL=bootstrap-reboot.css.map */ |
@ -0,0 +1,8 @@ | |||
/*! | |||
* Bootstrap Reboot v4.5.3 (https://getbootstrap.com/) | |||
* Copyright 2011-2020 The Bootstrap Authors | |||
* Copyright 2011-2020 Twitter, Inc. | |||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) | |||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) | |||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} | |||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */ |
@ -0,0 +1,211 @@ | |||
//实现表格信息的分页操作 | |||
//显示模态框 | |||
function display_form(homeworkname) { | |||
//模态框显示事件 | |||
$('#homeworkName').text(homeworkname) | |||
$("#formWindow").modal('show') | |||
} | |||
function clock(){ | |||
$('.countdown').text(minusOne($('.countdown').text())) | |||
} | |||
function minusOne(time){ | |||
var date=Date.parse(time) | |||
date-=1000 | |||
return Date(date) | |||
} | |||
function jumpto(url) { | |||
var token = window.sessionStorage.getItem('token') | |||
if (token != null) { | |||
$.ajax({ | |||
async: true, | |||
type: "GET", | |||
url: url, | |||
dataType: "html",//返回整合HTML | |||
headers: { | |||
"token": token//此处放置请求到的用户token | |||
}, | |||
mimeType: "multipart/form-data", | |||
contentType: false, | |||
cache: false, | |||
processData: false, | |||
success: function (data) { | |||
$("html").html(data);//刷新整个body页面的html | |||
}, | |||
error: function () { | |||
var tk = 100; | |||
tk = tk + 100; | |||
} | |||
}) | |||
} | |||
else { | |||
window.location.replace("admin/login") | |||
} | |||
} | |||
function studentList() { | |||
$('#homeworkName').text("hihi") | |||
$("#studentList").modal('show') | |||
} | |||
function cancelHomework(homeworkName){ | |||
var token = window.sessionStorage.getItem('token'); | |||
$.ajax({ | |||
async: false, | |||
type: "POST", | |||
url: "/admin/cancel/"+homeworkName, | |||
headers: { | |||
"token": token//此处放置请求到的用户token | |||
}, | |||
dataType: "JSON", | |||
mimeType: "multipart/form-data", | |||
contentType: false, | |||
cache: false, | |||
processData: false, | |||
success: function (data) { | |||
swal("删除成功") | |||
}, | |||
error: function () { | |||
swal("网络异常,请稍后重试") | |||
} | |||
}); | |||
jumpto("/admin"); | |||
} | |||
function submit_new_studentList() { | |||
var form = document.getElementById("upload"); | |||
var formData = new FormData(form); | |||
var fileInput = $('#file').get(0).files[0]; | |||
if (!fileInput) { | |||
return | |||
} | |||
var token = window.sessionStorage.getItem('token'); | |||
//发送ajax请求 | |||
$.ajax({ | |||
async: false, | |||
type: "POST", | |||
url: "/admin/studentListFile", | |||
data: formData, | |||
headers: { | |||
"token": token//此处放置请求到的用户token | |||
}, | |||
dataType: "JSON", | |||
mimeType: "multipart/form-data", | |||
contentType: false, | |||
cache: false, | |||
processData: false, | |||
success: function (data) { | |||
swal("上传成功") | |||
}, | |||
error: function () { | |||
swal("网络异常,请稍后重试") | |||
} | |||
}) | |||
jumpto("/admin/studentList"); | |||
} | |||
function login() { | |||
//模态框显示事件 | |||
var form = document.getElementById("login_form"); | |||
var formData = new FormData(form); | |||
$.ajax({ | |||
async: false, | |||
type: "POST", | |||
url: "/admin/verify", | |||
data: formData, | |||
dataType: "JSON", | |||
mimeType: "multipart/form-data", | |||
contentType: false, | |||
cache: false, | |||
processData: false, | |||
success: function (data) { | |||
if (data["result"]) { | |||
swal("登录成功"); | |||
//window.location.replace("/admin"); | |||
window.sessionStorage.setItem('token', data['token']); | |||
jumpto("/admin") | |||
} | |||
else { | |||
swal("密码有误") | |||
} | |||
}, | |||
error: function () { | |||
swal("网络异常,请稍后重试") | |||
} | |||
}) | |||
} | |||
//提交表格value | |||
function submit_form() { | |||
var form = document.getElementById("upload"); | |||
var formData = new FormData(form); | |||
var homeworkname = document.getElementById("homeworkName").textContent; | |||
formData.append("homeworkName", homeworkname) | |||
var name = formData.get("name") | |||
var num = formData.get("num") | |||
//用户名为2-5个汉字 | |||
var name_reg = /^[\u4e00-\u9fa5]{2,5}$/ | |||
//学号为8位数字 | |||
var num_reg = /^\d{11}$/ | |||
if (!name_reg.test(name)) { | |||
swal("用户名错误!") | |||
return | |||
} | |||
if (!num_reg.test(num)) { | |||
swal("学号错误!") | |||
return | |||
} | |||
var fileInput = $('#file').get(0).files[0]; | |||
if (!fileInput) { | |||
swal("请选择上传文件!") | |||
return | |||
} | |||
//发送ajax请求 | |||
$.ajax({ | |||
async: false, | |||
type: "POST", | |||
url: "/upload", | |||
data: formData, | |||
dataType: "JSON", | |||
mimeType: "multipart/form-data", | |||
contentType: false, | |||
cache: false, | |||
processData: false, | |||
success: function (data) { | |||
swal("上传成功") | |||
}, | |||
error: function () { | |||
swal("网络异常,请稍后重试") | |||
} | |||
}) | |||
} | |||
function publish(){ | |||
var form = document.getElementById("upload"); | |||
var formData = new FormData(form); | |||
var token = window.sessionStorage.getItem('token'); | |||
$.ajax({ | |||
async: false, | |||
type: "POST", | |||
headers: { | |||
"token": token//此处放置请求到的用户token | |||
}, | |||
url: "/admin/newHomework", | |||
data: formData, | |||
dataType: "JSON", | |||
mimeType: "multipart/form-data", | |||
contentType: false, | |||
cache: false, | |||
processData: false, | |||
success: function (data) { | |||
swal("发布成功") | |||
}, | |||
error: function () { | |||
swal("网络异常,请稍后重试") | |||
} | |||
}); | |||
jumpto("/admin"); | |||
} |
@ -0,0 +1,4 @@ | |||
12345678910,刘思 | |||
12345678911,王五 | |||
10174900160,蔡闻宇 | |||
12345678920,汤姆 |
@ -0,0 +1,100 @@ | |||
{% extends "root.html" %} | |||
{% block Navi %} | |||
<!-- Navigation --> | |||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> | |||
<div class="container"> | |||
<a class="navbar-brand" href="javascript:void(0);" onclick="jumpto('/admin')">作业提交admin</a> | |||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" | |||
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" | |||
aria-label="Toggle navigation"> | |||
<span class="navbar-toggler-icon"></span> | |||
</button> | |||
<div class="collapse navbar-collapse" id="navbarResponsive"> | |||
<ul class="navbar-nav ml-auto"> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="javascript:void(0);" onclick="jumpto('/admin/studentList')">导入学生名单</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="javascript:void(0);" onclick="jumpto('/admin/publish')">发布新作业</a> | |||
</li> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="/contact">Contact</a> | |||
</li> | |||
</ul> | |||
</div> | |||
</div> | |||
</nav> | |||
{% endblock %} | |||
{% block main %} | |||
<div class="container"> | |||
<!-- Page Heading/Breadcrumbs --> | |||
<h1 class="mt-4 mb-3">作业一览 | |||
</h1> | |||
<div class="row"> | |||
<!-- Blog Entries Column --> | |||
<div class="col-md-8"> | |||
{% for homework in homeworks %} | |||
<!-- Blog Post --> | |||
<div class="card mb-4"> | |||
<div class="card-body"> | |||
<h2 class="card-title">{{homework["name"]}}</h2> | |||
<h2 class="card-title">DEADLINE:{{homework["deadline"]}}</h2> | |||
<p class="card-text">{{homework["describe"]}}</p> | |||
<button class="btn btn-primary" onclick="jumpto('/admin/{{homework['name']}}')">提交情况 | |||
→</button> | |||
<button class="btn btn-danger" onclick="cancelHomework('{{homework['name']}}')">取消 | |||
→</button> | |||
</div> | |||
</div> | |||
{% endfor %} | |||
</div> | |||
</div> | |||
<!-- /.row --> | |||
</div> | |||
{% endblock %} | |||
{% block form %} | |||
<div class="modal fade" id="studentList" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |||
<div class="modal-dialog"> | |||
<div class="modal-content"> | |||
<div class="modal-header"> | |||
<h4 class="modal-title" id="homeworkName"> | |||
作业名称 | |||
</h4> | |||
</div> | |||
<div class="modal-body"> | |||
<!--上传信息表格--> | |||
<form id="upload" action="/upload" enctype='multipart/form-data' method='POST'> | |||
<div class="input-group mb-3"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text">姓名</span> | |||
</div> | |||
<label for="name"></label> | |||
<input type="text" class="form-control" id="name" name="name"> | |||
</div> | |||
<div class="input-group mb-3"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text">学号</span> | |||
</div> | |||
<label for="num"></label> | |||
<input type="text" class="form-control" id="num" name="num"> | |||
</div> | |||
<!--上传文件的控件--> | |||
<input id='file' class="btn btn-info" name="file" type="file"> | |||
<div class="modal-footer"> | |||
<button type="button" onclick="submit_form()" class="btn btn-primary">提交</button> | |||
<button type="button" class="btn btn-danger" data-dismiss="modal">关闭</button> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{% endblock %} |
@ -0,0 +1,24 @@ | |||
{% extends "root.html" %} | |||
{% block main %} | |||
<div class="container"> | |||
<div class="form row"> | |||
<form class="form-horizontal col-sm-offset-3 col-md-offset-3" id="login_form" > | |||
<h3 class="form-title">Login</h3> | |||
<div class="col-sm-9 col-md-9"> | |||
<div class="form-group"> | |||
<i class="fa fa-user fa-lg"></i> | |||
<input class="form-control required" type="text" placeholder="Username" name="username" autofocus="autofocus" maxlength="20"/> | |||
</div> | |||
<div class="form-group"> | |||
<i class="fa fa-lock fa-lg"></i> | |||
<input class="form-control required" type="password" placeholder="Password" name="password" maxlength="8"/> | |||
</div> | |||
<div class="form-group"> | |||
<button type="button" class="btn btn-success pull-right" onclick="login()">login</button> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
{% endblock %} |
@ -0,0 +1,20 @@ | |||
{% extends "root.html" %} | |||
{% block main %} | |||
<div class="container"> | |||
<!-- Page Heading/Breadcrumbs --> | |||
<h1 class="mt-4 mb-3">联系方式 | |||
</h1> | |||
<div class="row"> | |||
<!-- Blog Entries Column --> | |||
<div class="col-md-8"> | |||
<a>微信:*****</a> | |||
<a>邮箱: *****</a> | |||
</div> | |||
</div> | |||
<!-- /.row --> | |||
</div> | |||
{% endblock %} |
@ -0,0 +1,35 @@ | |||
{% extends "admin.html" %} | |||
{% block main %} | |||
<div class="container"> | |||
<!--提交信息的表格--> | |||
<h1 class="mt-4 mb-3">{{homeworkName}} | |||
</h1> | |||
<table class="table table-hover" id="stuInfoTable"> | |||
<thead> | |||
<tr> | |||
<th>提交状态</th> | |||
<th>学号</th> | |||
<th>姓名</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{% for i in data %} | |||
<tr> | |||
{% if i[2] == 'yes' %} | |||
<td style="color: green"> | |||
<strong>已提交</strong> | |||
</td> | |||
{% else %} | |||
<td style="color: red"> | |||
<strong>未提交</strong> | |||
</td> | |||
{% endif %} | |||
<td>{{ i[0] }}</td> | |||
<td>{{ i[1] }}</td> | |||
</tr> | |||
{% endfor %} | |||
</tbody> | |||
</table> | |||
</div> | |||
{% endblock %} |
@ -0,0 +1,60 @@ | |||
{% extends "admin.html" %} | |||
{% block main %} | |||
<div class="container"> | |||
<div class="modal-dialog"> | |||
<div class="modal-content"> | |||
<div class="modal-header"> | |||
<h4 class="modal-title" id="homeworkName"> | |||
发布新作业 | |||
</h4> | |||
</div> | |||
<div class="modal-body"> | |||
<!--上传信息表格--> | |||
<form id="upload" enctype='multipart/form-data' method='POST'> | |||
<div class="input-group mb-3"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text">作业名称</span> | |||
</div> | |||
<label for="name"></label> | |||
<input type="text" class="form-control" id="name" name="name"> | |||
</div> | |||
<div class="input-group mb-3"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text">作业描述</span> | |||
</div> | |||
<label for="describe"></label> | |||
<input type="text" class="form-control" id="describe" name="describe" style="height:50px;width100px;"> | |||
</div> | |||
<div class="input-group mb-3"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text">截止时间</span> | |||
</div> | |||
<label for="deadline"></label> | |||
<input type="text" class="form-control" id="deadline" name="deadline"> | |||
</div> | |||
<div class="modal-footer"> | |||
<button type="button" onclick="publish()" class="btn btn-primary">提交</button> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<script type="text/javascript"> | |||
$('.form_datetime').datetimepicker({ | |||
//language: 'fr', | |||
language: 'zh-CN', | |||
//format : 'yyyy-mm-dd hh:ii:ss',//日期格式。可以将日期格式,定成年月日时分秒。 | |||
format: 'yyyy-mm-dd hh:00:00',//日期格式。可以将日期格式,定成年月日时,分秒为0。 | |||
weekStart: 1, | |||
todayBtn: 1, | |||
autoclose: 1, | |||
todayHighlight: 1, | |||
startView: 2, | |||
forceParse: 0, | |||
showMeridian: 1 | |||
}); | |||
</script> | |||
{% endblock %} |
@ -0,0 +1,174 @@ | |||
<html> | |||
{% block head %} | |||
<head> | |||
<META HTTP-EQUIV="pragma" CONTENT="no-cache"> | |||
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> | |||
<META HTTP-EQUIV="expires" CONTENT="0"> | |||
<link rel="shortcut icon" href="#" /> | |||
<!-- Bootstrap --> | |||
<!-- 最新版本的 Bootstrap 核心 CSS 文件 --> | |||
<!-- Bootstrap core CSS --> | |||
<link href="/static/bootstrap/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> | |||
<!-- Custom styles for this template --> | |||
<link href="/static/bootstrap/css/modern-business.css" rel="stylesheet"> | |||
<script src="/static/form.js"></script> | |||
<script src="https://unpkg.zhimg.com/sweetalert/dist/sweetalert.min.js"></script> | |||
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"> | |||
</script> | |||
<script src="/static/bootstrap/vendor/jquery/jquery.min.js"></script> | |||
<script src="/static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.min.js"></script> | |||
<!--如果页面模板在后台传入了标题,那页面的标题就是(传入标题+ '- 测试平台'),否则就是'测试平台'--> | |||
<title>作业提交网站</title> | |||
</head> | |||
{% endblock %} | |||
<body> | |||
{% block Navi %} | |||
<!-- Navigation --> | |||
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark fixed-top"> | |||
<div class="container"> | |||
<a class="navbar-brand" href="/">作业提交</a> | |||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" | |||
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" | |||
aria-label="Toggle navigation"> | |||
<span class="navbar-toggler-icon"></span> | |||
</button> | |||
<div class="collapse navbar-collapse" id="navbarResponsive"> | |||
<ul class="navbar-nav ml-auto"> | |||
<li class="nav-item"> | |||
<a class="nav-link" href="contact">Contact</a> | |||
</li> | |||
</ul> | |||
</div> | |||
</div> | |||
</nav> | |||
{% endblock %} | |||
<!-- Page Content --> | |||
{% block main %} | |||
<div class="container"> | |||
<!-- Page Heading/Breadcrumbs --> | |||
<h1 class="mt-4 mb-3">作业一览 | |||
</h1> | |||
<div class="row"> | |||
<!-- Blog Entries Column --> | |||
<div class="col-md-8"> | |||
{% for homework in homeworks %} | |||
<!-- Blog Post --> | |||
<div class="card mb-4"> | |||
<div class="card-body"> | |||
<h2 class="card-title">{{homework["name"]}}</h2> | |||
<p class="card-text">{{homework["describe"]}}</p> | |||
<button class="btn btn-primary" onclick="display_form('{{homework['name']}}')">Submit | |||
→</button> | |||
</div> | |||
<a class="card-footer text-muted">DEADLINE:{{homework["deadline"]}}<a> | |||
<h2 class="card-footer countdown">{{homework["deadline"]}}</h2> | |||
</div> | |||
{% endfor %} | |||
</div> | |||
</div> | |||
<!-- /.row --> | |||
</div> | |||
{% endblock %} | |||
<!-- /.container --> | |||
<!-- Footer --> | |||
{% block footer %} | |||
<footer class="py-5 bg-dark"> | |||
<div class="container"> | |||
<p class="m-0 text-center text-white">Copyright © 2021</p> | |||
</div> | |||
<!-- /.container --> | |||
</footer> | |||
{% endblock %} | |||
{% block form %} | |||
<div class="modal fade" id="formWindow" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" | |||
aria-hidden="true"> | |||
<div class="modal-dialog"> | |||
<div class="modal-content"> | |||
<div class="modal-header"> | |||
<h4 class="modal-title" id="homeworkName"> | |||
作业名称 | |||
</h4> | |||
</div> | |||
<div class="modal-body"> | |||
<!--上传信息表格--> | |||
<form id="upload" action="/upload" enctype='multipart/form-data' method='POST'> | |||
<div class="input-group mb-3"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text">姓名</span> | |||
</div> | |||
<label for="name"></label> | |||
<input type="text" class="form-control" id="name" name="name"> | |||
</div> | |||
<div class="input-group mb-3"> | |||
<div class="input-group-prepend"> | |||
<span class="input-group-text">学号</span> | |||
</div> | |||
<label for="num"></label> | |||
<input type="text" class="form-control" id="num" name="num"> | |||
</div> | |||
<!--上传文件的控件--> | |||
<input id='file' class="btn btn-info" name="file" type="file"> | |||
<div class="modal-footer"> | |||
<button type="button" onclick="submit_form()" class="btn btn-primary">提交</button> | |||
<button type="button" class="btn btn-danger" data-dismiss="modal">关闭</button> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{% endblock %} | |||
</body> | |||
<script> | |||
function t2t(leave) { | |||
var day = Math.floor(leave / (1000 * 60 * 60 * 24)); | |||
var hour = Math.floor(leave / (1000 * 3600)) - (day * 24); | |||
var minute = Math.floor(leave / (1000 * 60)) - (day * 24 * 60) - (hour * 60); | |||
var second = Math.floor(leave / (1000)) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60); | |||
return day.toString() + "day " + hour.toString() + ":" + minute.toString() + ":" + second.toString() | |||
} | |||
function parseDate(stringInput) { | |||
var daySplit = stringInput.split("day "); | |||
var day = daySplit[0] | |||
var time = daySplit[1].split(":") | |||
var t = (1000 * 60 * 60 * 24) * parseInt(day) + (1000 * 3600) * parseInt(time[0]) + (1000 * 60) * parseInt(time[1]) + 1000 * parseInt(time[2]) | |||
return t | |||
} | |||
$().ready(function () { | |||
var now = Date.now() | |||
$(".countdown").each(function () { | |||
var deadline = Date.parse($(this).text()); | |||
var leave = deadline - now; | |||
$(this).text(t2t(leave)) | |||
}) | |||
setInterval(function (e) { | |||
$(".countdown").each(function () { | |||
var stringtime = $(this).text(); | |||
var leave = parseDate(stringtime) | |||
if (leave<1000){ | |||
leave=1000; | |||
} | |||
$(this).text(t2t(leave - 1000)) | |||
}) | |||
}, 1000); | |||
}) | |||
</script> | |||
</html> |
@ -0,0 +1,31 @@ | |||
{% extends "publish.html" %} | |||
{% block main %} | |||
<div class="container"> | |||
<!--提交信息的表格--> | |||
<h1 class="mt-4 mb-3">目前的学生列表 | |||
</h1> | |||
<table class="table table-hover" id="stuInfoTable"> | |||
<thead> | |||
<tr> | |||
<th>学号</th> | |||
<th>姓名</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{% for i in data %} | |||
<tr> | |||
<td>{{ i[0] }}</td> | |||
<td>{{ i[1] }}</td> | |||
</tr> | |||
{% endfor %} | |||
</tbody> | |||
</table> | |||
<form id="upload" enctype='multipart/form-data' method='POST'> | |||
<!--上传文件的控件--> | |||
<input id='file' class="btn btn-info" name="file" type="file"> | |||
<div class="modal-footer"> | |||
<button type="button" onclick="submit_new_studentList()" class="btn btn-primary">提交新的学生名单</button> | |||
</div> | |||
</form> | |||
</div> | |||
{% endblock %} |