@ -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 %} |