浏览代码

first commit

master
caiwenyu 3 年前
当前提交
7bf855927f
共有 45 个文件被更改,包括 47503 次插入0 次删除
  1. +3
    -0
      .gitignore
  2. 二进制
      __pycache__/xml.cpython-37.pyc
  3. +220
    -0
      backend.py
  4. 二进制
      bin/云计算第一次作业/10174900160_蔡闻宇_00-50-17.pdf
  5. 二进制
      bin/云计算第一次作业/10174900160_蔡闻宇_23-53-14.pdf
  6. 二进制
      cloud.zip
  7. +1
    -0
      homeworkMetadata.xml
  8. +22
    -0
      static/bootstrap/css/modern-business.css
  9. +75
    -0
      static/bootstrap/js/contact_me.js
  10. +912
    -0
      static/bootstrap/js/jqBootstrapValidation.js
  11. +3872
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-grid.css
  12. +1
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-grid.css.map
  13. +7
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-grid.min.css
  14. +1
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-grid.min.css.map
  15. +326
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.css
  16. +1
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.css.map
  17. +8
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.min.css
  18. +1
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.min.css.map
  19. +10263
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap.css
  20. +1
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap.css.map
  21. +7
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap.min.css
  22. +1
    -0
      static/bootstrap/vendor/bootstrap/css/bootstrap.min.css.map
  23. +7031
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.js
  24. +1
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.js.map
  25. +7
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.min.js
  26. +1
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.min.js.map
  27. +4418
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.js
  28. +1
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.js.map
  29. +7
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.min.js
  30. +1
    -0
      static/bootstrap/vendor/bootstrap/js/bootstrap.min.js.map
  31. +10872
    -0
      static/bootstrap/vendor/jquery/jquery.js
  32. +2
    -0
      static/bootstrap/vendor/jquery/jquery.min.js
  33. +1
    -0
      static/bootstrap/vendor/jquery/jquery.min.map
  34. +8777
    -0
      static/bootstrap/vendor/jquery/jquery.slim.js
  35. +2
    -0
      static/bootstrap/vendor/jquery/jquery.slim.min.js
  36. +1
    -0
      static/bootstrap/vendor/jquery/jquery.slim.min.map
  37. +211
    -0
      static/form.js
  38. +4
    -0
      studentList.csv
  39. +100
    -0
      templates/admin.html
  40. +24
    -0
      templates/adminLogin.html
  41. +20
    -0
      templates/contact.html
  42. +35
    -0
      templates/homework.html
  43. +60
    -0
      templates/publish.html
  44. +174
    -0
      templates/root.html
  45. +31
    -0
      templates/studentList.html

+ 3
- 0
.gitignore 查看文件

@ -0,0 +1,3 @@
# Windows:
__pycahce__
.vscode

二进制
__pycache__/xml.cpython-37.pyc 查看文件


+ 220
- 0
backend.py 查看文件

@ -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")

二进制
bin/云计算第一次作业/10174900160_蔡闻宇_00-50-17.pdf 查看文件


二进制
bin/云计算第一次作业/10174900160_蔡闻宇_23-53-14.pdf 查看文件


二进制
cloud.zip 查看文件


+ 1
- 0
homeworkMetadata.xml 查看文件

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

+ 22
- 0
static/bootstrap/css/modern-business.css 查看文件

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

+ 75
- 0
static/bootstrap/js/contact_me.js 查看文件

@ -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'>&times;")
.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'>&times;")
.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('');
});

+ 912
- 0
static/bootstrap/js/jqBootstrapValidation.js 查看文件

@ -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);
}
// ---------------------------------------------------------
// EMAIL
// ---------------------------------------------------------
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 );

+ 3872
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-grid.css
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-grid.css.map
文件差异内容过多而无法显示
查看文件


+ 7
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-grid.min.css
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-grid.min.css.map
文件差异内容过多而无法显示
查看文件


+ 326
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.css 查看文件

@ -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 */

+ 1
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.css.map
文件差异内容过多而无法显示
查看文件


+ 8
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.min.css 查看文件

@ -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 */

+ 1
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap-reboot.min.css.map
文件差异内容过多而无法显示
查看文件


+ 10263
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap.css
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap.css.map
文件差异内容过多而无法显示
查看文件


+ 7
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap.min.css
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/css/bootstrap.min.css.map
文件差异内容过多而无法显示
查看文件


+ 7031
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.js
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.js.map
文件差异内容过多而无法显示
查看文件


+ 7
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.min.js
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.bundle.min.js.map
文件差异内容过多而无法显示
查看文件


+ 4418
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.js
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.js.map
文件差异内容过多而无法显示
查看文件


+ 7
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.min.js
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/bootstrap/js/bootstrap.min.js.map
文件差异内容过多而无法显示
查看文件


+ 10872
- 0
static/bootstrap/vendor/jquery/jquery.js
文件差异内容过多而无法显示
查看文件


+ 2
- 0
static/bootstrap/vendor/jquery/jquery.min.js
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/jquery/jquery.min.map
文件差异内容过多而无法显示
查看文件


+ 8777
- 0
static/bootstrap/vendor/jquery/jquery.slim.js
文件差异内容过多而无法显示
查看文件


+ 2
- 0
static/bootstrap/vendor/jquery/jquery.slim.min.js
文件差异内容过多而无法显示
查看文件


+ 1
- 0
static/bootstrap/vendor/jquery/jquery.slim.min.map
文件差异内容过多而无法显示
查看文件


+ 211
- 0
static/form.js 查看文件

@ -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");
}

+ 4
- 0
studentList.csv 查看文件

@ -0,0 +1,4 @@
12345678910,刘思
12345678911,王五
10174900160,蔡闻宇
12345678920,汤姆

+ 100
- 0
templates/admin.html 查看文件

@ -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']}}')">提交情况
&rarr;</button>
<button class="btn btn-danger" onclick="cancelHomework('{{homework['name']}}')">取消
&rarr;</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 %}

+ 24
- 0
templates/adminLogin.html 查看文件

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

+ 20
- 0
templates/contact.html 查看文件

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

+ 35
- 0
templates/homework.html 查看文件

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

+ 60
- 0
templates/publish.html 查看文件

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

+ 174
- 0
templates/root.html 查看文件

@ -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
&rarr;</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 &copy; 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>

+ 31
- 0
templates/studentList.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 %}

正在加载...
取消
保存