Compare commits
210 Commits
a30a412aff
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| dbeeb4d25a | |||
| 58c4b85e6a | |||
| b6fc41994e | |||
| 1a08ba460c | |||
| 692631e395 | |||
| 1174b225a7 | |||
| ded28f6a1e | |||
| 4e64fb2422 | |||
| eb074e5dd5 | |||
| 6ef3faf149 | |||
| 9b519f4650 | |||
| d6bef5812b | |||
| 8e8fccbb1a | |||
| 237f0acf8d | |||
| 44ce96c7d9 | |||
| dae0ce21be | |||
| a94dc7b415 | |||
| ca44cf0eaa | |||
| 18c3968e59 | |||
| 5ce9ee1b1e | |||
| 2596680c3a | |||
| ff030444e4 | |||
| e14688471a | |||
| c554d2d7af | |||
| 9296b70ac3 | |||
| cff2b66bc0 | |||
| 9d68073dd5 | |||
| 894d075d55 | |||
| 8021f15e58 | |||
| b5461dbff4 | |||
| aab814a23a | |||
| c0d0e43e73 | |||
| 3e79a22956 | |||
| ca55d9d2fd | |||
| e655b72a5c | |||
| e75cce4dc9 | |||
| a2be2f6378 | |||
| b07880eb7d | |||
| 9048134f55 | |||
| dc2aedb06b | |||
| 74d63734bc | |||
| 70c0c7925d | |||
| 1dddf07b54 | |||
| 88f5713f16 | |||
| 7075531fc6 | |||
| 908fa9344b | |||
| 519d77d5b2 | |||
| d404ccfa78 | |||
| 1deec07d33 | |||
| a00a85efdb | |||
| feb6d1e2ce | |||
| 548120f9db | |||
| 964332cf44 | |||
| b5b5e92b73 | |||
| f59b43c99e | |||
| 68a126cb8b | |||
| dc66080c22 | |||
| f69c0a112f | |||
| d41e846ee4 | |||
| c7024d5851 | |||
| ada0a97a42 | |||
| fdd09d5e4d | |||
| 6ce370db9c | |||
| 1edb119983 | |||
| ad9fcf4781 | |||
| 7c13aa0e6c | |||
| 8f5b0e7dd2 | |||
| cffeebdec4 | |||
| 22e3b797cb | |||
| 514d86e77c | |||
| 172bbc1be3 | |||
| 1ebdfa44c8 | |||
| bb2104539c | |||
| af9fd8af11 | |||
| 0cf81af5e9 | |||
| 8d91463dd0 | |||
| d365c3c466 | |||
| 3e3d32c20e | |||
| df2c025c63 | |||
| f1fac2037a | |||
| ebd95f9413 | |||
| 5fb451fa97 | |||
| c708aaf381 | |||
| 0541e271f6 | |||
| 72758a4b95 | |||
| 0fde0df39c | |||
| e35abb4009 | |||
| a934ff1a80 | |||
| 973140e324 | |||
| bc4c4b6d7c | |||
| 2ab12bf44e | |||
| 035a072f9c | |||
| ad9bda48cc | |||
| c055ebe094 | |||
| 22dfab9424 | |||
| 5649e7e5a4 | |||
| edba8b2b10 | |||
| ba95a8e886 | |||
| ca8cbadccc | |||
| eaea8e1f0f | |||
| 638dded5a1 | |||
| 95bd7c7e65 | |||
| e11cddd05e | |||
| 1ed2e473e1 | |||
| 3694a91c74 | |||
| f3b0064dc7 | |||
| fca154ee23 | |||
| d7a82f6780 | |||
| 1bb907c71e | |||
| 20bb7bd1f6 | |||
| 756e2ca96e | |||
| 38c2e4ff18 | |||
| c497dbb421 | |||
| 4527403846 | |||
| a5ffdc32e1 | |||
| d0d6679e5a | |||
| c47b9e9450 | |||
| a4c6ae3d0c | |||
| 245d08d3cb | |||
| 4a176fb61f | |||
| aa707d4999 | |||
| f7f8e7d9f9 | |||
| c7a2d92d51 | |||
| 59dcdb8d13 | |||
| 58e96145a3 | |||
| ae52b1869c | |||
| 7e274c5add | |||
| 1d7ec89b80 | |||
| 9e172002c3 | |||
| fd1596e7d4 | |||
| bf789cb32d | |||
| 24848e7791 | |||
| 1b89233330 | |||
| d9d336f200 | |||
| f7fb8bdece | |||
| 652c95bff9 | |||
| cddb55c014 | |||
| 0557cd15c8 | |||
| 5d66d17fe2 | |||
| 8c79601d98 | |||
| 8aff863916 | |||
| 072a9d3359 | |||
| 0d3a099690 | |||
| d22e97e3fc | |||
| 5f9559ae19 | |||
| ef1f726fc6 | |||
| a55b7efd20 | |||
| b7fac7ca42 | |||
| 5f4be0635c | |||
| 2193e20ca8 | |||
| 0668a1e4d0 | |||
| b6d7d17a36 | |||
| 94a4d94bd1 | |||
| 7e8cbf5aaa | |||
| eaeeba42ef | |||
| 429e1d637c | |||
| 8edb1cc618 | |||
| 5387b3584b | |||
| 26dfd131f6 | |||
| 892196a827 | |||
| c73997452b | |||
| 234d9cf5b8 | |||
| 05e0e82736 | |||
| 9c4d5b8a7d | |||
| a303a65156 | |||
| 6487490ad7 | |||
| 5915f30e08 | |||
| 138e1a221e | |||
| e172d78c30 | |||
| caefaa4472 | |||
| 6770e100ff | |||
| 08790e3e9e | |||
| 6026eac50e | |||
| 29bce0557e | |||
| e3ac6e7839 | |||
| 50dc888339 | |||
| c180dad115 | |||
| 1511faecde | |||
| 8cda76cc65 | |||
| 83350552a5 | |||
| 01aa0afd3e | |||
| 0f0aa99c0d | |||
| f4eb448476 | |||
| f3ef70b096 | |||
| b863ac4c8c | |||
| ba6aa5fbb5 | |||
| b47b0cb8b9 | |||
| f5e4386ba3 | |||
| bb53d199bd | |||
| b9d18cbe17 | |||
| 93ce602837 | |||
| e2efd8b029 | |||
| 95b3aea9a2 | |||
| fc8569c81d | |||
| a75ff25ba4 | |||
| f0647238c4 | |||
| 7bf2cfb68a | |||
| cfa14f34be | |||
| 4a045b7fc6 | |||
| 10bb0b8f77 | |||
| 2def6b2353 | |||
| b94d91d8b5 | |||
| 4bad46e43e | |||
| 80d7b89cfa | |||
| a8bfa76a6c | |||
| 4c8d30da70 | |||
| 97a56e0e2f | |||
| 08ea4fb4e6 | |||
| a0af99031d | |||
| eeba5e0dfa |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -13,3 +13,8 @@ dist/
|
|||||||
build/
|
build/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.idea/
|
.idea/
|
||||||
|
.idea/*
|
||||||
|
|
||||||
|
.idea/SmartRollCall.iml
|
||||||
|
*.DS_Store
|
||||||
|
.DS_Store
|
||||||
|
|||||||
2
.idea/SmartRollCall.iml
generated
2
.idea/SmartRollCall.iml
generated
@@ -7,7 +7,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.11 (venv)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.11 (SmartRollCall)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="jquery" level="application" />
|
<orderEntry type="library" name="jquery" level="application" />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (venv)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (SmartRollCall)" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
||||||
46
app/app.py
46
app/app.py
@@ -1,46 +1,2 @@
|
|||||||
from flask import Flask, request, redirect, url_for, render_template, session, jsonify
|
import views
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.secret_key = 'sUNiJ7QPulxrbmZD'
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
# 如果用户已登录,则重定向到主页;否则,重定向到登录页面
|
|
||||||
if 'username' in session:
|
|
||||||
return redirect(url_for('home'))
|
|
||||||
else:
|
|
||||||
return render_template('login.html')
|
|
||||||
@app.route('/register', methods=['GET','POST'])
|
|
||||||
def register():
|
|
||||||
return render_template('register.html')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/login', methods=['POST'])
|
|
||||||
def login():
|
|
||||||
username = request.form['username']
|
|
||||||
password = request.form['password']
|
|
||||||
print(username,password)
|
|
||||||
# 验证用户名和密码...
|
|
||||||
if valid_login(username, password):
|
|
||||||
# 登录成功
|
|
||||||
session['username'] = username
|
|
||||||
return jsonify(success=True, message="登录成功")
|
|
||||||
else:
|
|
||||||
# 登录失败
|
|
||||||
return jsonify(success=False, message="无效的用户名或密码")
|
|
||||||
|
|
||||||
|
|
||||||
def valid_login(username, password):
|
|
||||||
# 这里应该是验证用户名和密码的逻辑,比如查询数据库等等
|
|
||||||
# 假设用户名是admin且密码是secret
|
|
||||||
return username == 'admin' and password == '1'
|
|
||||||
|
|
||||||
@app.route('/home')
|
|
||||||
def home():
|
|
||||||
if 'username' in session:
|
|
||||||
return '你已登录,欢迎, %s!' % session['username']
|
|
||||||
else:
|
|
||||||
return redirect(url_for('login')) # 如果用户未登录,重定向到登录页面
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(debug=True)
|
|
||||||
|
|||||||
60
app/static/js/get_teacher_attendance_table.js
Normal file
60
app/static/js/get_teacher_attendance_table.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
layui.use(['laypage', 'element', 'jquery', 'dropdown'], function () {
|
||||||
|
var laypage = layui.laypage;
|
||||||
|
var $ = layui.jquery;
|
||||||
|
|
||||||
|
function renderTable(page) {
|
||||||
|
$.get('/api/get-teacher-attendance-table?page=' + page, function (response) {
|
||||||
|
var $tbody = $('#attendanceTable tbody');
|
||||||
|
$tbody.empty(); // 清空表格内容
|
||||||
|
|
||||||
|
response.data.forEach(function (item) {
|
||||||
|
var $row = $('<tr></tr>');
|
||||||
|
$row.append(`<td>${item.course_id}</td>`);
|
||||||
|
$row.append(`<td>${item.course_name}</td>`);
|
||||||
|
$row.append(`<td>${item.class_name}</td>`);
|
||||||
|
$row.append(`<td>${item.major}</td>`);
|
||||||
|
$row.append(`<td><div class="layui-btn-container">
|
||||||
|
<button type="button" class="layui-btn btn-sign-in"
|
||||||
|
data-course-id="${item.course_id}"
|
||||||
|
data-course-name="${item.course_name}"
|
||||||
|
data-class-name = "${item.class_name}"
|
||||||
|
data-major-id = "${item.major_id}"
|
||||||
|
>签到</button>
|
||||||
|
</div>
|
||||||
|
</td>`)
|
||||||
|
$tbody.append($row);
|
||||||
|
});
|
||||||
|
|
||||||
|
laypage.render({
|
||||||
|
elem: 'pagination',
|
||||||
|
count: response.count,
|
||||||
|
limit: 10,
|
||||||
|
curr: page,
|
||||||
|
jump: function (obj, first) {
|
||||||
|
if (!first) {
|
||||||
|
renderTable(obj.curr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为动态生成的按钮添加点击事件
|
||||||
|
$(document).on('click', '.btn-sign-in', function () {
|
||||||
|
var courseId = $(this).data('course-id'); // 获取课程ID
|
||||||
|
var courseName = $(this).data('course-name'); // 获取课程名
|
||||||
|
var className = $(this).data('class-name'); // 获取课程名
|
||||||
|
var majorId = $(this).data('major-id'); // 获取课程名
|
||||||
|
// console.log(courseId,courseName,className,majorId)
|
||||||
|
// 向后端发送POST请求
|
||||||
|
$.post('/api/teacher-sign-in', {
|
||||||
|
course_id: courseId,
|
||||||
|
course_name: courseName,
|
||||||
|
class_name: className,
|
||||||
|
major_id:majorId
|
||||||
|
}, function (response) {
|
||||||
|
layer.msg(response.msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
renderTable(1); // 初始加载第一页
|
||||||
|
});
|
||||||
9
app/static/js/logout.js
Normal file
9
app/static/js/logout.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
var $ = layui.jquery; // 获取Layui的jQuery对象
|
||||||
|
|
||||||
|
$('#logoutLink').on('click', function () {
|
||||||
|
// 向后端发送登出请求
|
||||||
|
$.get('/logout', function (data) {
|
||||||
|
// 重定向到登录页面,或根据后端响应做其他处理
|
||||||
|
window.location.href = '/login';
|
||||||
|
});
|
||||||
|
});
|
||||||
11
app/static/js/menu.js
Normal file
11
app/static/js/menu.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
$.get('/api/menu', function (menuItems) {
|
||||||
|
var menuList = $('.layui-nav.layui-nav-tree');
|
||||||
|
menuList.empty();
|
||||||
|
|
||||||
|
menuItems.forEach(function (item) {
|
||||||
|
var href = item.path || 'javascript:void(0);'; // 提供一个默认值
|
||||||
|
menuList.append('<li class="layui-nav-item"><a href="' + href + '">' + item.name + '</a></li>');
|
||||||
|
});
|
||||||
|
|
||||||
|
layui.element.render('nav', 'test');
|
||||||
|
});
|
||||||
45
app/static/js/upload_excel.js
Normal file
45
app/static/js/upload_excel.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// 获取上传按钮和文件输入元素
|
||||||
|
var uploadBtn = document.getElementById('uploadExcel');
|
||||||
|
var fileInput = document.getElementById('excelFile');
|
||||||
|
|
||||||
|
// 当点击上传按钮时触发文件输入的点击事件
|
||||||
|
uploadBtn.addEventListener('click', function () {
|
||||||
|
fileInput.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理文件选择事件
|
||||||
|
fileInput.addEventListener('change', function () {
|
||||||
|
var file = this.files[0]; // 获取文件对象
|
||||||
|
if (file) {
|
||||||
|
// 检查文件类型
|
||||||
|
var fileName = file.name;
|
||||||
|
var fileExt = fileName.split('.').pop().toLowerCase();
|
||||||
|
if (fileExt === 'xlsx' || fileExt === 'xls') {
|
||||||
|
// 使用 FormData 上传文件
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('file', file, fileName); // 'file' 是你的服务器端期待的字段名
|
||||||
|
|
||||||
|
// 使用 fetch 发送文件
|
||||||
|
fetch('/api/receive-excel', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData // 传递表单数据
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json(); // 如果上传成功,解析JSON响应
|
||||||
|
}
|
||||||
|
throw new Error('Network response was not ok.'); // 如果上传失败,抛出错误
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
// 处理响应数据
|
||||||
|
layer.msg('上传成功!'); // 弹出成功消息
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Upload failed:', error);
|
||||||
|
layer.msg("文件上传失败!"); // 弹出失败消息
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
alert("请上传Excel文件!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
88
app/templates/announcement.html
Normal file
88
app/templates/announcement.html
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>公告信息</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="static/css/layui.css" rel="stylesheet"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.name }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<div class="layui-container">
|
||||||
|
<div class="layui-row">
|
||||||
|
<div class="layui-col-xs12">
|
||||||
|
<h1 class="layui-header">公告</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-row">
|
||||||
|
<div class="layui-col-md6">
|
||||||
|
<fieldset class="layui-elem-field">
|
||||||
|
<legend>今日课程</legend>
|
||||||
|
<div class="layui-field-box" id="courses-container">
|
||||||
|
<!-- 课程信息将通过JavaScript动态添加 -->
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
// 请求后端获取课程信息
|
||||||
|
$.get('/api/get-today-courses', function (data) {
|
||||||
|
|
||||||
|
if (data.msg == "ok") {
|
||||||
|
data.data.forEach(function (course) {
|
||||||
|
// 为每个课程创建一个段落<p>并添加到容器中,并设置样式使字体更大且更加好看
|
||||||
|
$('#courses-container').append('<p style="font-size: 1.2em; margin: 5px 0;">课程: ' + course.course_name + ' <br> 时间: ' + course.time + '</p>');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#courses-container').append(data.msg)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
185
app/templates/attendance-reminder.html
Normal file
185
app/templates/attendance-reminder.html
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>签到提醒</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="static/css/layui.css" rel="stylesheet"/>
|
||||||
|
<style>
|
||||||
|
.calendar-table {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-table td {
|
||||||
|
font-size: 20px; /* 增加日期的字体大小 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.signed {
|
||||||
|
color: #fff; /* 白色文字 */
|
||||||
|
background-color: green; /* 已签到日期的背景色 */
|
||||||
|
}
|
||||||
|
|
||||||
|
#current-time {
|
||||||
|
color: #16baaa;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#attendance-reminder {
|
||||||
|
color: #16baaa;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reminder {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-table th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-box {
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required-sign {
|
||||||
|
background-color: #FFA07A; /* 需要签到日期的背景色 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.name }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<div>
|
||||||
|
<!-- 动态显示当前时间的标题 -->
|
||||||
|
<h2 id="attendance-reminder">签到提醒 </h2>
|
||||||
|
<h2 id="current-time">当前时间</h2>
|
||||||
|
<table class="layui-table calendar-table" id="calendar">
|
||||||
|
<!-- 日历的头部 -->
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>日</th>
|
||||||
|
<th>一</th>
|
||||||
|
<th>二</th>
|
||||||
|
<th>三</th>
|
||||||
|
<th>四</th>
|
||||||
|
<th>五</th>
|
||||||
|
<th>六</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<!-- 日历的主体部分 -->
|
||||||
|
<tbody id="calendar-body">
|
||||||
|
<!-- 动态生成日历的日期部分 -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!-- 提醒内容 -->
|
||||||
|
<div class="reminder">
|
||||||
|
<span><span class="color-box" style="background-color: green;"></span>绿色代表当月已签到</span><br>
|
||||||
|
<span><span class="color-box" style="background-color: #FFA07A;"></span>颜色代表本月需要签到</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script>
|
||||||
|
layui.use(['layer'], function () {
|
||||||
|
var layer = layui.layer;
|
||||||
|
|
||||||
|
// 假设的已签到日期数据
|
||||||
|
var signedDays = [1, 5, 9]; // 这里仅为示例,实际应从服务器获取
|
||||||
|
var requiredSignDays = [2, 6, 15, 23];
|
||||||
|
|
||||||
|
// 动态生成日历
|
||||||
|
function generateCalendar() {
|
||||||
|
var today = new Date();
|
||||||
|
var currentMonth = today.getMonth();
|
||||||
|
var currentYear = today.getFullYear();
|
||||||
|
|
||||||
|
var firstDay = new Date(currentYear, currentMonth, 1).getDay();
|
||||||
|
var daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
|
||||||
|
|
||||||
|
var calendarHtml = '';
|
||||||
|
var day = 1;
|
||||||
|
|
||||||
|
for (var i = 0; i < 6; i++) {
|
||||||
|
calendarHtml += '<tr>';
|
||||||
|
for (var j = 0; j < 7; j++) {
|
||||||
|
if (i === 0 && j < firstDay) {
|
||||||
|
calendarHtml += '<td></td>';
|
||||||
|
} else if (day > daysInMonth) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
var classes = '';
|
||||||
|
if (signedDays.includes(day)) classes += 'signed '; // 已签到
|
||||||
|
if (requiredSignDays.includes(day)) classes += 'required-sign'; // 需要签到
|
||||||
|
calendarHtml += `<td class="${classes}">${day}</td>`;
|
||||||
|
day++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calendarHtml += '</tr>';
|
||||||
|
if (day > daysInMonth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('calendar-body').innerHTML = calendarHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTime() {
|
||||||
|
var now = new Date();
|
||||||
|
var timeStr = now.getFullYear() + '年' + (now.getMonth() + 1) + '月' + now.getDate() + '日';
|
||||||
|
document.getElementById('current-time').innerText = '当前时间: ' + timeStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时生成日历
|
||||||
|
generateCalendar();
|
||||||
|
updateTime();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
81
app/templates/attendance-teacher.html
Normal file
81
app/templates/attendance-teacher.html
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>课程签到</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="/static/css/layui.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-left">
|
||||||
|
<!-- 移动端显示 -->
|
||||||
|
<li class="layui-nav-item layui-show-xs-inline-block layui-hide-sm" lay-header-event="menuLeft">
|
||||||
|
<i class="layui-icon layui-icon-spread-left"></i>
|
||||||
|
</li>
|
||||||
|
<li class="layui-nav-item layui-hide-xs"><a href="/attendance-teacher/attendance">签到</a></li>
|
||||||
|
<li class="layui-nav-item layui-hide-xs"><a href="/attendance-teacher/import-class">导入班级</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.name }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<table id="attendanceTable" class="layui-table">
|
||||||
|
<!-- 表头 -->
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>课程代码</th>
|
||||||
|
<th>课程名称</th>
|
||||||
|
<th>班级</th>
|
||||||
|
<th>专业</th>
|
||||||
|
<th>签到</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- 表格数据将在这里填充 -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div id="pagination"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="/static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script src="/static/js/get_teacher_attendance_table.js"></script>
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
105
app/templates/attendance.html
Normal file
105
app/templates/attendance.html
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>课程签到</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="static/css/layui.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.name }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<div id="sign-in-reminder" class="layui-container">
|
||||||
|
<blockquote class="layui-elem-quote layui-text" id="title">
|
||||||
|
课程签到
|
||||||
|
</blockquote>
|
||||||
|
<div class="layui-text" id="course-info"></div>
|
||||||
|
<div class="layui-row" style="margin-top: 20px;">
|
||||||
|
<div class="layui-col-xs12">
|
||||||
|
<button class="layui-btn" id="sign-in-btn">立即签到</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
let courseData = {};
|
||||||
|
// 获取课程名称或状态
|
||||||
|
$.get("/api/get-course-name", function (response) {
|
||||||
|
|
||||||
|
if (response.msg === "ok") {
|
||||||
|
// 如果后端返回课程名
|
||||||
|
$("#course-info").text("课程:" + response.data.course_name + "。在上课时间内,请及时签到!");
|
||||||
|
// 启用签到按钮
|
||||||
|
$("#sign-in-btn").prop('disabled', false);
|
||||||
|
courseData = response.data;
|
||||||
|
} else {
|
||||||
|
// 根据不同的消息更新状态
|
||||||
|
$("#course-info").text(response.msg); // 显示没有课程的消息
|
||||||
|
// 禁用签到按钮
|
||||||
|
$("#sign-in-btn").prop('disabled', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 绑定签到按钮事件
|
||||||
|
$("#sign-in-btn").click(function () {
|
||||||
|
if (!$(this).prop('disabled')) {
|
||||||
|
// 发送签到请求到后端
|
||||||
|
$.post("/api/student-sign-in",courseData,function (response) {
|
||||||
|
// 处理签到后的响应
|
||||||
|
if (response.msg === 'ok') {
|
||||||
|
layer.msg('签到成功!');
|
||||||
|
} else {
|
||||||
|
layer.msg(response.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layer.msg("当前不可签到"); // Or handle disabled button click as needed
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
88
app/templates/course-category.html
Normal file
88
app/templates/course-category.html
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>课程类别</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="static/css/layui.css" rel="stylesheet"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.name }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<table id="courseTable" lay-filter="courseTableFilter"></table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script>
|
||||||
|
// 请求后端获取菜单数据
|
||||||
|
|
||||||
|
|
||||||
|
layui.use(['table', 'jquery'], function () {
|
||||||
|
var table = layui.table;
|
||||||
|
var $ = layui.jquery;
|
||||||
|
|
||||||
|
// 发起GET请求获取数据
|
||||||
|
$.get('/api/get-course-type', function (res) {
|
||||||
|
// 假设返回的res是一个对象,包含必修和选修的课程名数组
|
||||||
|
|
||||||
|
// 处理返回的数据,转换为表格能接受的格式
|
||||||
|
var tableData = [];
|
||||||
|
res['必修'].forEach(function (course) {
|
||||||
|
tableData.push({course_type: '必修', course_name: course});
|
||||||
|
});
|
||||||
|
res['选修'].forEach(function (course) {
|
||||||
|
tableData.push({course_type: '选修', course_name: course});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染表格
|
||||||
|
table.render({
|
||||||
|
elem: '#courseTable',
|
||||||
|
cols: [[ // 设置表头
|
||||||
|
{field: 'course_type', title: '课程类型', sort: true},
|
||||||
|
{field: 'course_name', title: '课程名称', sort: true}
|
||||||
|
]],
|
||||||
|
data: tableData // 使用处理后的数据
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
96
app/templates/course-info.html
Normal file
96
app/templates/course-info.html
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>课程信息</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="static/css/layui.css" rel="stylesheet"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.name }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<!-- 内容主体区域 -->
|
||||||
|
<div style="padding: 15px;">
|
||||||
|
<blockquote class="layui-elem-quote layui-text">
|
||||||
|
课程信息
|
||||||
|
</blockquote>
|
||||||
|
<table class="layui-table">
|
||||||
|
<colgroup>
|
||||||
|
<col width="150">
|
||||||
|
<col width="150">
|
||||||
|
<col width="150">
|
||||||
|
<col width="100">
|
||||||
|
<col>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>课程名称</th>
|
||||||
|
<th>课程代码</th>
|
||||||
|
<th>学分</th>
|
||||||
|
<th>课程描述</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- 动态生成的课程信息将填充在这里 -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script>
|
||||||
|
$.get('/api/get-course-info', function (courses) {
|
||||||
|
var tbody = $('.layui-table tbody');
|
||||||
|
tbody.empty(); // 清空表格现有内容
|
||||||
|
courses.forEach(function (course) {
|
||||||
|
var row = '<tr>' +
|
||||||
|
'<td>' + course.course_name + '</td>' +
|
||||||
|
'<td>' + course.course_code + '</td>' +
|
||||||
|
'<td>' + course.credits + '</td>' +
|
||||||
|
'<td>' + course.description + '</td>' +
|
||||||
|
'</tr>';
|
||||||
|
tbody.append(row); // 将新行添加到表格中
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
91
app/templates/forget.html
Normal file
91
app/templates/forget.html
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>重置</title>
|
||||||
|
<link rel="stylesheet" href="static/css/layui.css" media="all">
|
||||||
|
<style>
|
||||||
|
.layui-form {width: 450px; margin: 21px auto 0;}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="layui-container">
|
||||||
|
<div class="layui-row">
|
||||||
|
<div class="layui-col-xs12 layui-col-sm12 layui-col-md12">
|
||||||
|
<form class="layui-form" style="margin-top: 20px;">
|
||||||
|
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-inline">
|
||||||
|
<label class="layui-form-label">验证手机号</label>
|
||||||
|
<div class="layui-input-inline layui-input-wrap">
|
||||||
|
<input type="tel" name="phone" lay-verify="required|phone" autocomplete="off"
|
||||||
|
lay-reqtext="请填写手机号" lay-affix="clear" class="layui-input demo-phone">
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-mid" style="padding: 0!important;">
|
||||||
|
<button type="button" class="layui-btn layui-btn-primary" lay-on="get-vercode">获取验证码
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">验证码</label>
|
||||||
|
<div class="layui-input-inline layui-input-wrap">
|
||||||
|
<input type="text" name="vercode" lay-verify="required" autocomplete="off" lay-affix="clear"
|
||||||
|
class="layui-input">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">新密码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="password" name="newPassword" required lay-verify="required"
|
||||||
|
placeholder="请输入新密码"
|
||||||
|
autocomplete="off" class="layui-input" id="newPassword">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">确认密码</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="password" name="confirmPassword" required lay-verify="required"
|
||||||
|
placeholder="请确认新密码"
|
||||||
|
autocomplete="off" class="layui-input">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button class="layui-btn" lay-submit lay-filter="formDemo">重置密码</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="static/layui.js"></script>
|
||||||
|
<script>
|
||||||
|
layui.use(['form'], function () {
|
||||||
|
var form = layui.form;
|
||||||
|
|
||||||
|
// 这里可以写一些表单验证逻辑
|
||||||
|
form.verify({
|
||||||
|
// 例如确认密码的逻辑
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听提交
|
||||||
|
form.on('submit(formDemo)', function (data) {
|
||||||
|
// 发送Ajax请求到后端API处理重置密码
|
||||||
|
// ...
|
||||||
|
|
||||||
|
return false; // 阻止表单跳转
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
54
app/templates/home.html
Normal file
54
app/templates/home.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>首页</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="static/css/layui.css" rel="stylesheet"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.nickname }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
92
app/templates/import-class.html
Normal file
92
app/templates/import-class.html
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>课程签到</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="/static/css/layui.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black">网上上课点名系统</div>
|
||||||
|
<!-- 头部区域(可配合layui 已有的水平导航) -->
|
||||||
|
<ul class="layui-nav layui-layout-left">
|
||||||
|
<!-- 移动端显示 -->
|
||||||
|
<li class="layui-nav-item layui-show-xs-inline-block layui-hide-sm" lay-header-event="menuLeft">
|
||||||
|
<i class="layui-icon layui-icon-spread-left"></i>
|
||||||
|
</li>
|
||||||
|
<li class="layui-nav-item layui-hide-xs"><a href="/attendance-teacher/attendance">签到</a></li>
|
||||||
|
<li class="layui-nav-item layui-hide-xs"><a href="/attendance-teacher/import-class">导入班级</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="layui-nav layui-layout-right">
|
||||||
|
<li class="layui-nav-item layui-hide layui-show-sm-inline-block">
|
||||||
|
<a href="javascript:;">
|
||||||
|
<img
|
||||||
|
src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png"
|
||||||
|
class="layui-nav-img"
|
||||||
|
/>
|
||||||
|
{{ session.name }}
|
||||||
|
</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd><a href="/home/profile">资料</a></dd> <!-- 修改这里的href指向/profile -->
|
||||||
|
<dd><a href="javascript:;" id="logoutLink">登出</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="layui-nav-item"
|
||||||
|
lay-header-event="menuRight"
|
||||||
|
lay-unselect
|
||||||
|
></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<!-- 内容主体区域 -->
|
||||||
|
<div style="margin: 20px;">
|
||||||
|
<div class="layui-row">
|
||||||
|
<div class="layui-col-md12">
|
||||||
|
<fieldset class="layui-elem-field">
|
||||||
|
<legend>学生信息上传 - 要求</legend>
|
||||||
|
<div class="layui-field-box">
|
||||||
|
<p>请按照以下格式准备Excel文件:</p>
|
||||||
|
<ul>
|
||||||
|
<li>必须包含表头:班级、姓名、学号、课程、专业</li>
|
||||||
|
<li>确保数据格式正确无误</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>注:</b>错误的数据格式可能导致上传失败</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="layui-col-md12" style="padding-top: 20px;">
|
||||||
|
<!-- 隐藏的文件输入,用户选择文件 -->
|
||||||
|
<input type="file" id="excelFile" style="display:none;" accept=".xlsx, .xls">
|
||||||
|
<!-- 上传按钮 -->
|
||||||
|
<button type="button" class="layui-btn" id="uploadExcel">上传Excel文件</button>
|
||||||
|
<!-- 下载模板链接 -->
|
||||||
|
<a href="/files/template.xlsx" class="layui-btn layui-btn-primary">下载模板</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/jquery.min.js"></script>
|
||||||
|
<script src="/static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
<script src="/static/js/logout.js"></script>
|
||||||
|
<script src="/static/js/upload_excel.js"></script>
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
<div class="layui-input-prefix">
|
<div class="layui-input-prefix">
|
||||||
<i class="layui-icon layui-icon-username"></i>
|
<i class="layui-icon layui-icon-username"></i>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="username" value="" lay-verify="required" placeholder="用户名"
|
<input type="text" name="number" value="" lay-verify="required" placeholder="学号/工号"
|
||||||
lay-reqtext="请填写用户名" autocomplete="off" class="layui-input" lay-affix="clear">
|
lay-reqtext="学号/工号" autocomplete="off" class="layui-input" lay-affix="clear">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<input type="checkbox" name="remember" lay-skin="primary" title="记住密码">
|
<input type="checkbox" name="remember" lay-skin="primary" title="记住密码">
|
||||||
<a href="#forget" style="float: right; margin-top: 7px;">忘记密码?</a>
|
<a href="forget" style="float: right; margin-top: 7px;">忘记密码?</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<!-- 登录按钮 -->
|
<!-- 登录按钮 -->
|
||||||
@@ -64,11 +64,9 @@
|
|||||||
data: data.field, // 表单数据
|
data: data.field, // 表单数据
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
// 根据返回的结果进行操作
|
console.log(response)
|
||||||
// 如果成功,可能会重定向到其他页面
|
|
||||||
// 如果失败,可能会显示错误信息
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
window.location.href = '/home'; // 或者你的成功页面
|
window.location.href = '/attendance-reminder'; // 或者你的成功页面
|
||||||
} else {
|
} else {
|
||||||
console.log(response.message)
|
console.log(response.message)
|
||||||
layer.alert('登录失败: ' + response.message);
|
layer.alert('登录失败: ' + response.message);
|
||||||
@@ -87,7 +85,5 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
60
app/templates/profile.html
Normal file
60
app/templates/profile.html
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8"/>
|
||||||
|
<title>资料</title>
|
||||||
|
<meta name="renderer" content="webkit"/>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
|
<link href="/static/css/layui.css" rel="stylesheet"/>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
<div class="layui-header">
|
||||||
|
<div class="layui-logo layui-hide-xs layui-bg-black" href="/home">网上上课点名系统</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-body">
|
||||||
|
<!-- 内容主体区域 -->
|
||||||
|
<div style="padding: 15px">
|
||||||
|
<blockquote class="layui-elem-quote layui-text">
|
||||||
|
更换头像和昵称
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<!-- 更换头像和昵称的表单 -->
|
||||||
|
<form id="profileForm" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">头像:</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="file" name="avatar"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button class="layui-btn" type="submit">更新</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="layui-side layui-bg-black">
|
||||||
|
<div class="layui-side-scroll">
|
||||||
|
<!-- 动态加载菜单栏 -->
|
||||||
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/static/jquery.min.js"></script> <!-- 确保已经引入jQuery -->
|
||||||
|
<script src="/static/layui.js"></script>
|
||||||
|
<script src="/static/js/menu.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -12,8 +12,9 @@
|
|||||||
width: 320px;
|
width: 320px;
|
||||||
margin: 21px auto 0;
|
margin: 21px auto 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.demo-reg-item {
|
.demo-reg-item {
|
||||||
margin-bottom: 10px; /* 调整间距 */
|
margin-bottom: 10px; /* 调整间距 */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
<div class="layui-input-prefix">
|
<div class="layui-input-prefix">
|
||||||
<i class="layui-icon layui-icon-username"></i>
|
<i class="layui-icon layui-icon-username"></i>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="nickname" value="" lay-verify="required" placeholder="昵称"
|
<input type="text" name="name" value="" lay-verify="required" placeholder="姓名"
|
||||||
autocomplete="off" class="layui-input" lay-affix="clear">
|
autocomplete="off" class="layui-input" lay-affix="clear">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,8 +36,9 @@
|
|||||||
<div class="layui-input-prefix">
|
<div class="layui-input-prefix">
|
||||||
<i class="layui-icon layui-icon-cellphone"></i>
|
<i class="layui-icon layui-icon-cellphone"></i>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="cellphone" value="" lay-verify="required|phone" placeholder="手机号"
|
<input type="text" name="number" value="" placeholder="学号X开头/工号G开头"
|
||||||
lay-reqtext="请填写手机号" autocomplete="off" class="layui-input" id="reg-cellphone">
|
lay-reqtext="请填写学号/工号" autocomplete="off" class="layui-input" id="reg-number"
|
||||||
|
lay-verify="customRule">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item demo-reg-item">
|
<div class="layui-form-item demo-reg-item">
|
||||||
@@ -58,12 +60,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<button class="layui-btn layui-btn-fluid" lay-submit lay-filter="demo-reg">注册</button>
|
<button class="layui-btn layui-btn-fluid" type="submit" lay-submit lay-filter="btnRegister">注册</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script src="static/layui.js"></script>
|
<script src="static/layui.js"></script>
|
||||||
|
<script src="static/jquery.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
layui.use(function () {
|
layui.use(function () {
|
||||||
var $ = layui.$;
|
var $ = layui.$;
|
||||||
@@ -77,15 +80,72 @@
|
|||||||
if (value !== passwordValue) {
|
if (value !== passwordValue) {
|
||||||
return '两次密码输入不一致';
|
return '两次密码输入不一致';
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
customRule: function (value, item) {
|
||||||
|
// 首先检查是否以X或G开头
|
||||||
|
if (!value.startsWith('X') && !value.startsWith('G')) {
|
||||||
|
return '学号必须以X开头,工号必须以G开头';
|
||||||
|
}
|
||||||
|
// 如果以X开头,检查长度是否为13位
|
||||||
|
if (value.startsWith('X') && value.length !== 13) {
|
||||||
|
return '学号长度必须为13位';
|
||||||
|
}
|
||||||
|
// 如果以G开头,检查长度是否为5位
|
||||||
|
if (value.startsWith('G') && value.length !== 5) {
|
||||||
|
return '工号长度必须为5位';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
form.on('submit(demo-reg)', function (data) {
|
layui.use(['form', 'layer'], function () {
|
||||||
// AJAX 请求和其他逻辑...
|
var form = layui.form;
|
||||||
return false;
|
var layer = layui.layer;
|
||||||
|
|
||||||
|
// 监听注册表单的提交事件
|
||||||
|
form.on('submit(btnRegister)', function (data) {
|
||||||
|
// 构建要提交的数据对象
|
||||||
|
var postData = {
|
||||||
|
name: data.field.name, // 昵称
|
||||||
|
number: data.field.number, // 手机号
|
||||||
|
password: data.field.password, // 密码
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送AJAX POST请求
|
||||||
|
console.log("Sending AJAX request to /register", postData);
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/register", // 修改为你的注册接口
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify(postData), // 将数据转为JSON字符串
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
// 根据返回的结果进行操作
|
||||||
|
if (response.success) {
|
||||||
|
layer.msg('注册成功!', {
|
||||||
|
time: 2000 //2秒关闭(如果不配置,默认是3秒)
|
||||||
|
}, function () {
|
||||||
|
// 关闭后执行的回调函数
|
||||||
|
window.location.href = '/';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 显示错误信息
|
||||||
|
layer.alert('注册失败: ' + response.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
layer.alert('注册请求失败');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false; // 阻止表单的默认提交
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
xw
|
||||||
43
app/tests/ReceiveExcelTest.py
Normal file
43
app/tests/ReceiveExcelTest.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
|
||||||
|
from app.views import *
|
||||||
|
from werkzeug.datastructures import FileStorage # 用于创建测试文件
|
||||||
|
|
||||||
|
class ReceiveExcelTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
app.testing = True # 设置 Flask 应用为测试模式
|
||||||
|
self.client = app.test_client()
|
||||||
|
# 创建一个测试用的Excel文件或使用一个真实的Excel文件路径
|
||||||
|
self.test_file_path = 'template.xlsx'
|
||||||
|
|
||||||
|
def test_receive_excel_no_file(self):
|
||||||
|
# 测试没有文件时的情况
|
||||||
|
response = self.client.post('/api/receive-excel')
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
self.assertEqual(response.get_json(), {"error": "No file part"})
|
||||||
|
|
||||||
|
def test_receive_excel_with_file(self):
|
||||||
|
# 模拟已登录用户
|
||||||
|
with self.client.session_transaction() as sess:
|
||||||
|
sess['number'] = 'X202301000001'
|
||||||
|
|
||||||
|
# 使用 FileStorage 创建测试文件对象
|
||||||
|
data = {
|
||||||
|
'file': (open(self.test_file_path, 'rb'), self.test_file_path)
|
||||||
|
}
|
||||||
|
# 发送 POST 请求到 /api/receive-excel
|
||||||
|
response = self.client.post('/api/receive-excel', data=data, content_type='multipart/form-data')
|
||||||
|
|
||||||
|
# 验证返回的状态码和响应
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(response.get_json(), {"message": "File successfully processed"})
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
# 清理测试用的文件或数据
|
||||||
|
if os.path.exists(self.test_file_path):
|
||||||
|
os.remove(self.test_file_path)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
39
app/tests/StudentSignInTest.py
Normal file
39
app/tests/StudentSignInTest.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from app.views import *
|
||||||
|
from unittest.mock import patch
|
||||||
|
from flask import session
|
||||||
|
|
||||||
|
|
||||||
|
class StudentSignInTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# 设置 Flask 测试模式
|
||||||
|
app.testing = True
|
||||||
|
self.client = app.test_client()
|
||||||
|
|
||||||
|
@patch('db.database_manager.DatabaseManager.update_sign_in_info')
|
||||||
|
def test_student_sign_in(self, mock_update_sign_in_info):
|
||||||
|
# 模拟已登录用户
|
||||||
|
with self.client.session_transaction() as sess:
|
||||||
|
sess['number'] = 'X202301000001'
|
||||||
|
|
||||||
|
# 模拟数据库操作
|
||||||
|
mock_update_sign_in_info.return_value = 1 # 假设成功更新签到信息
|
||||||
|
|
||||||
|
# 发送 POST 请求到/api/student-sign-in
|
||||||
|
response = self.client.post('/api/student-sign-in', data={
|
||||||
|
'course_name': '计算机导论',
|
||||||
|
'course_id': '2',
|
||||||
|
'student_number': 'X202301000001',
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
# 验证返回的状态码和JSON数据
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
json_data = response.get_json()
|
||||||
|
self.assertEqual(json_data, {"msg": "ok", "data": "签到成功!"})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
37
app/tests/TeacherSignInTest.py
Normal file
37
app/tests/TeacherSignInTest.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from app.views import *
|
||||||
|
from unittest.mock import patch
|
||||||
|
from flask import session
|
||||||
|
|
||||||
|
class TeacherSignInTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# 设置 Flask 测试模式
|
||||||
|
app.testing = True
|
||||||
|
self.client = app.test_client()
|
||||||
|
|
||||||
|
@patch('db.database_manager.DatabaseManager.update_sign_in_info')
|
||||||
|
def test_teacher_sign_in(self, mock_teacher_sign_in):
|
||||||
|
# 模拟已登录用户
|
||||||
|
with self.client.session_transaction() as sess:
|
||||||
|
sess['number'] = 'X202301000001'
|
||||||
|
|
||||||
|
# 模拟数据库操作的返回值
|
||||||
|
mock_teacher_sign_in.return_value = {"msg": "当前班级专业没有学生'"}
|
||||||
|
|
||||||
|
# 发送 POST 请求到/api/teacher-sign-in
|
||||||
|
response = self.client.post('/api/teacher-sign-in', data={
|
||||||
|
'course_id': '123',
|
||||||
|
'course_name': 'Test Course',
|
||||||
|
'class_name': 'TestClass',
|
||||||
|
'major_id': '456'
|
||||||
|
})
|
||||||
|
|
||||||
|
# 验证返回的状态码和数据
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
json_data = response.get_json()
|
||||||
|
self.assertEqual(json_data, {"msg": "当前班级专业没有学生"})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
73
app/tests/TestGetHtml.py
Normal file
73
app/tests/TestGetHtml.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from app.views import *
|
||||||
|
|
||||||
|
|
||||||
|
class HtmlTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
app.testing = True # 设置 Flask 应用为测试模式
|
||||||
|
self.client = app.test_client() # 创建一个测试客户端
|
||||||
|
|
||||||
|
def test_get_course_info(self):
|
||||||
|
# 测试 GET 请求
|
||||||
|
response = self.client.get('/course-info')
|
||||||
|
|
||||||
|
# 验证响应状态码
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
# 验证响应的Content-Type头部值
|
||||||
|
self.assertIn('text/html', response.content_type)
|
||||||
|
|
||||||
|
def test_get_attendance(self):
|
||||||
|
# 测试 GET 请求
|
||||||
|
response = self.client.get('/attendance')
|
||||||
|
|
||||||
|
# 验证响应状态码
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# 验证响应的Content-Type头部值
|
||||||
|
self.assertIn('text/html', response.content_type)
|
||||||
|
|
||||||
|
def test_get_announcement(self):
|
||||||
|
# 测试 GET 请求
|
||||||
|
response = self.client.get('/announcement')
|
||||||
|
|
||||||
|
# 验证响应状态码
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# 验证响应的Content-Type头部值
|
||||||
|
self.assertIn('text/html', response.content_type)
|
||||||
|
|
||||||
|
def test_get_attendance_reminder(self):
|
||||||
|
# 测试 GET 请求
|
||||||
|
response = self.client.get('/attendance-reminder')
|
||||||
|
|
||||||
|
# 验证响应状态码
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# 验证响应的Content-Type头部值
|
||||||
|
self.assertIn('text/html', response.content_type)
|
||||||
|
|
||||||
|
def test_get_course_category(self):
|
||||||
|
# 测试 GET 请求
|
||||||
|
response = self.client.get('/course-category')
|
||||||
|
|
||||||
|
# 验证响应状态码
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# 验证响应的Content-Type头部值
|
||||||
|
self.assertIn('text/html', response.content_type)
|
||||||
|
|
||||||
|
def test_get_attendance_teacher(self):
|
||||||
|
# 测试 GET 请求
|
||||||
|
response = self.client.get('/attendance-teacher')
|
||||||
|
|
||||||
|
# 验证响应状态码
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
# 验证响应的Content-Type头部值
|
||||||
|
self.assertIn('text/html', response.content_type)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
44
app/tests/TestGetMenu.py
Normal file
44
app/tests/TestGetMenu.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from app.views import *
|
||||||
|
from flask import session
|
||||||
|
from unittest.mock import patch # 如果需要模拟数据库方法
|
||||||
|
|
||||||
|
|
||||||
|
class MenuAPITestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
app.testing = True # 设置 Flask 应用为测试模式
|
||||||
|
self.client = app.test_client() # 创建一个测试客户端
|
||||||
|
self.expected_menu = [
|
||||||
|
{"name": "课程类别", "path": "/course-category"},
|
||||||
|
{"name": "课程信息", "path": "/course-info"},
|
||||||
|
{"name": "课程签到", "path": "/attendance-teacher"},
|
||||||
|
{"name": "签到提醒", "path": "/attendance-reminder"}
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_menu_no_login(self):
|
||||||
|
# 测试未登录情况下请求API
|
||||||
|
response = self.client.get('/api/menu')
|
||||||
|
self.assertEqual(response.status_code, 401)
|
||||||
|
self.assertEqual(response.get_json(), [])
|
||||||
|
|
||||||
|
def test_login(self):
|
||||||
|
with self.client:
|
||||||
|
self.client.post('/login', data=dict(
|
||||||
|
number='G0001',
|
||||||
|
password='1'
|
||||||
|
))
|
||||||
|
|
||||||
|
# 确认登录后session中有数据
|
||||||
|
self.assertIn('role', session)
|
||||||
|
|
||||||
|
# 执行logout
|
||||||
|
response = self.client.get('/api/menu')
|
||||||
|
print(response.get_json())
|
||||||
|
# 确认响应是重定向到登录页面
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(self.expected_menu,response.get_json())
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
45
app/tests/TestLogin.py
Normal file
45
app/tests/TestLogin.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from app.views import *
|
||||||
|
from db.database_manager import DatabaseManager # 确保你可以导入DatabaseManager
|
||||||
|
|
||||||
|
class LoginTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# 设置 Flask 测试模式
|
||||||
|
app.testing = True
|
||||||
|
self.client = app.test_client()
|
||||||
|
|
||||||
|
def test_login_get(self):
|
||||||
|
# 测试 GET 请求返回登录页面
|
||||||
|
response = self.client.get('/login')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertIn('text/html', response.content_type)
|
||||||
|
|
||||||
|
def test_successful_login_post(self):
|
||||||
|
# 测试有效的登录 POST 请求
|
||||||
|
with self.client:
|
||||||
|
response = self.client.post('/login', data={
|
||||||
|
'number': 'G0001',
|
||||||
|
'password': '1'
|
||||||
|
})
|
||||||
|
# 根据你的应用逻辑调整断言
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_invalid_login_post(self):
|
||||||
|
# 测试无效的登录 POST 请求
|
||||||
|
response = self.client.post('/login', data={
|
||||||
|
'number': 'admin',
|
||||||
|
'password': 'admin'
|
||||||
|
})
|
||||||
|
# 将返回的数据解析为JSON
|
||||||
|
json_data = response.get_json()
|
||||||
|
|
||||||
|
# 确认JSON响应中的值
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertFalse(json_data['success'])
|
||||||
|
self.assertEqual(json_data['message'], "无效的用户名或密码")
|
||||||
|
|
||||||
|
# 运行测试
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
34
app/tests/TestLogout.py
Normal file
34
app/tests/TestLogout.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from app.views import *
|
||||||
|
|
||||||
|
class LogoutTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# 设置 Flask 测试模式
|
||||||
|
app.testing = True
|
||||||
|
self.client = app.test_client()
|
||||||
|
|
||||||
|
def test_logout(self):
|
||||||
|
# 先登录,确保 session 是设置的
|
||||||
|
with self.client:
|
||||||
|
self.client.post('/login', data=dict(
|
||||||
|
number='G0001',
|
||||||
|
password='1'
|
||||||
|
))
|
||||||
|
|
||||||
|
# 确认登录后session中有数据
|
||||||
|
self.assertIn('number', session)
|
||||||
|
|
||||||
|
# 执行logout
|
||||||
|
response = self.client.get('/logout')
|
||||||
|
|
||||||
|
# 确认 session 被清空
|
||||||
|
self.assertNotIn('number', session)
|
||||||
|
|
||||||
|
# 确认响应是重定向到登录页面
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertTrue(response.location.endswith('/login'))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
403
app/views.py
403
app/views.py
@@ -0,0 +1,403 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# 第三方库
|
||||||
|
import openpyxl
|
||||||
|
from flask import Flask, redirect, url_for, render_template, session, jsonify, request, send_file
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# 应用内部模块
|
||||||
|
from utils.time_utils import check_now_time
|
||||||
|
from utils.parse_table import parse_table
|
||||||
|
from utils.allowed_files import allowed_excel
|
||||||
|
from db.connection import MySQLPool
|
||||||
|
from db.database_manager import DatabaseManager
|
||||||
|
from models.Student import Student
|
||||||
|
from models.Teacher import Teacher
|
||||||
|
from models.User import User
|
||||||
|
from config import SECRET_KEY, LOGGING_CONFIG, FILE_PATH
|
||||||
|
|
||||||
|
app = Flask(__name__, static_folder='static')
|
||||||
|
app.secret_key = SECRET_KEY # 从配置文件设置
|
||||||
|
logging.basicConfig(**LOGGING_CONFIG)
|
||||||
|
|
||||||
|
# 一个全局MySQLPool对象,用于管理数据库连接
|
||||||
|
mysql_pool = MySQLPool()
|
||||||
|
# 配置文件路径,例如指向一个 'files' 目录
|
||||||
|
app.config['FILE_PATH'] = FILE_PATH
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
# 如果用户已登录,则重定向到主页;否则,重定向到登录页面
|
||||||
|
if 'number' in session:
|
||||||
|
return redirect(url_for('home'))
|
||||||
|
else:
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
if request.method == 'POST':
|
||||||
|
data = request.get_json()
|
||||||
|
print(data)
|
||||||
|
user = User(
|
||||||
|
name=data.get('name'),
|
||||||
|
number=data.get('number'),
|
||||||
|
password=data.get('password'), # 确保密码安全处理
|
||||||
|
status=True
|
||||||
|
)
|
||||||
|
identity = check_identity(user.number)
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
if not db_manager.user_exists(user.number):
|
||||||
|
db_manager.insert_user(user)
|
||||||
|
user_id = db_manager.query_user_id(user.number)
|
||||||
|
if identity == "teacher":
|
||||||
|
teacher = Teacher(name=user.name, teacher_number=user.number, user_id=user_id)
|
||||||
|
db_manager.insert_teacher(teacher)
|
||||||
|
else: # assumed student
|
||||||
|
class_name = user.number[1:5] + "级" + user.number[5:7] + "班"
|
||||||
|
major_id = user.number[7:10]
|
||||||
|
student = Student(student_name=user.name, student_number=user.number, user_id=user_id,
|
||||||
|
major_id=major_id,
|
||||||
|
class_name=class_name)
|
||||||
|
db_manager.insert_student(student)
|
||||||
|
return jsonify({"success": True, "message": "注册成功"})
|
||||||
|
else:
|
||||||
|
return jsonify({"success": False, "message": "用户已存在"})
|
||||||
|
else:
|
||||||
|
return render_template('register.html')
|
||||||
|
|
||||||
|
|
||||||
|
def check_identity(number):
|
||||||
|
identity = None
|
||||||
|
if number[0] == 'G':
|
||||||
|
identity = "teacher"
|
||||||
|
return identity
|
||||||
|
else:
|
||||||
|
identity = "student"
|
||||||
|
return identity
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template('login.html')
|
||||||
|
else:
|
||||||
|
number = request.form['number']
|
||||||
|
password = request.form['password']
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
|
||||||
|
result = db_manager.valid_login(number, password) # 获取验证结果
|
||||||
|
print(result)
|
||||||
|
# 确保用户已验证且活跃(未被禁用)
|
||||||
|
if result['valid'] and result['status'] == 1:
|
||||||
|
# 登录成功
|
||||||
|
session['number'] = number
|
||||||
|
session['role'] = check_identity(number)
|
||||||
|
session['name'] = result['name']
|
||||||
|
return jsonify(success=True, message="登录成功")
|
||||||
|
elif not result.get('status'):
|
||||||
|
# 用户被禁用的情况
|
||||||
|
return jsonify(success=False, message="账户已被禁用")
|
||||||
|
else:
|
||||||
|
# 其他登录失败情况
|
||||||
|
return jsonify(success=False, message="无效的用户名或密码")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/forget', methods=['GET', 'POST'])
|
||||||
|
def forget_page():
|
||||||
|
return render_template('forget.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/home')
|
||||||
|
def home():
|
||||||
|
if 'number' in session:
|
||||||
|
return render_template('home.html')
|
||||||
|
else:
|
||||||
|
return redirect("login")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
# 清除session中的所有信息
|
||||||
|
session.clear()
|
||||||
|
# 返回一个响应,或者重定向到登录页面
|
||||||
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/menu')
|
||||||
|
def get_menu():
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
|
||||||
|
# 从session中获取用户角色
|
||||||
|
if 'role' in session:
|
||||||
|
role = session['role']
|
||||||
|
menu_items = db_manager.get_menu(role)
|
||||||
|
|
||||||
|
# 转换菜单项为期望的格式并返回
|
||||||
|
formatted_menu_items = [
|
||||||
|
{"name": item['menu_name'], "path": item['path']} for item in menu_items
|
||||||
|
]
|
||||||
|
return jsonify(formatted_menu_items)
|
||||||
|
|
||||||
|
# 如果没有角色信息,可能用户未登录或session过期
|
||||||
|
return jsonify([]), 401 # 未授权状态码
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/home/profile', methods=['GET', 'POST'])
|
||||||
|
def profile():
|
||||||
|
if request.method == 'POST':
|
||||||
|
# 从表单获取数据
|
||||||
|
nickname = request.form['nickname']
|
||||||
|
avatar = request.files['avatar']
|
||||||
|
|
||||||
|
# 处理头像和昵称更新逻辑
|
||||||
|
# ...
|
||||||
|
|
||||||
|
return "资料更新成功" # 或者重定向到其他页面
|
||||||
|
|
||||||
|
# 如果是GET请求,显示表单页面
|
||||||
|
return render_template('profile.html') # 确保这里渲染的是包含上面表单的HTML页面
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/course-info', methods=['GET', 'POST'])
|
||||||
|
def course_info():
|
||||||
|
if request.method == "GET":
|
||||||
|
return render_template('course-info.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/get-course-info', methods=['GET'])
|
||||||
|
def get_course_info():
|
||||||
|
student_number = session.get('number')
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
if check_identity(student_number) == "student":
|
||||||
|
course_data = db_manager.get_class_courses(student_number)
|
||||||
|
return jsonify(course_data)
|
||||||
|
else:
|
||||||
|
course_data = db_manager.get_all_courses()
|
||||||
|
return jsonify(course_data)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/get-course-type', methods=['GET'])
|
||||||
|
def get_course_type():
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
course_data = db_manager.get_course_type()
|
||||||
|
# 创建存储必修和选修课程名称的字典
|
||||||
|
course_info = {"必修": [], "选修": []}
|
||||||
|
for course in course_data:
|
||||||
|
if course['course_type'] == '必修':
|
||||||
|
course_info["必修"].append(course['course_name'])
|
||||||
|
elif course['course_type'] == '选修':
|
||||||
|
course_info["选修"].append(course['course_name'])
|
||||||
|
|
||||||
|
return jsonify(course_info)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/get-announcement-info', methods=['GET'])
|
||||||
|
def get_announcement_info():
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
announcement_info = db_manager.get_announcement_info()
|
||||||
|
|
||||||
|
return jsonify(course_info)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/attendance', methods=['GET', 'POST'])
|
||||||
|
def course_checkin():
|
||||||
|
return render_template('attendance.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/announcement', methods=['GET', 'POST'])
|
||||||
|
def announcement():
|
||||||
|
return render_template('announcement.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/attendance-teacher', methods=['GET', 'POST'])
|
||||||
|
def announcement_teacher():
|
||||||
|
return render_template('attendance-teacher.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/attendance-reminder', methods=['GET', 'POST'])
|
||||||
|
def attendance_reminder():
|
||||||
|
return render_template('attendance-reminder.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/course-category', methods=['GET', 'POST'])
|
||||||
|
def course_category():
|
||||||
|
return render_template('course-category.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/attendance-teacher/import-class', methods=['GET'])
|
||||||
|
def import_class():
|
||||||
|
return render_template('import-class.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/attendance-teacher/attendance', methods=['GET'])
|
||||||
|
def teacher_attendance():
|
||||||
|
return render_template('attendance-teacher.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/get-teacher-attendance-table', methods=['GET'])
|
||||||
|
def get_current_teacher_courses():
|
||||||
|
number = session.get('number')
|
||||||
|
# 获取分页参数
|
||||||
|
page = request.args.get('page', 1, type=int) # 如果没有提供,默认为第一页
|
||||||
|
limit = request.args.get('limit', 10, type=int) # 如果没有提供,默认每页10条
|
||||||
|
|
||||||
|
# 获取所有课程数据
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
all_course_data = db_manager.get_current_teacher_courses(number)
|
||||||
|
print(all_course_data)
|
||||||
|
logging.info(f"all_course_data: {all_course_data}")
|
||||||
|
# 计算分页的起始和结束索引
|
||||||
|
start = (page - 1) * limit
|
||||||
|
end = start + limit
|
||||||
|
|
||||||
|
# 获取当前页的数据
|
||||||
|
current_page_data = all_course_data[start:end]
|
||||||
|
|
||||||
|
# 构建响应字典
|
||||||
|
response = {
|
||||||
|
'msg': 'ok' if current_page_data else 'no_data',
|
||||||
|
'count': len(all_course_data), # 数据的总数
|
||||||
|
'data': current_page_data # 当前页的课程信息列表
|
||||||
|
}
|
||||||
|
|
||||||
|
# 将查询结果转换为JSON格式并返回
|
||||||
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/files/template.xlsx')
|
||||||
|
def download_template():
|
||||||
|
# 确保这个路径是你的文件实际所在的服务器路径
|
||||||
|
path = os.path.join(app.config['FILE_PATH'], "template.xlsx")
|
||||||
|
return send_file(path, as_attachment=True)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/receive-excel', methods=['POST'])
|
||||||
|
def receive_excel():
|
||||||
|
number = session.get('number')
|
||||||
|
# 检查是否有文件在请求中
|
||||||
|
if 'file' not in request.files:
|
||||||
|
return jsonify({"error": "No file part"}), 400
|
||||||
|
|
||||||
|
file = request.files['file']
|
||||||
|
|
||||||
|
# 如果用户没有选择文件,浏览器也会提交一个空的文件名
|
||||||
|
if file.filename == '':
|
||||||
|
return jsonify({"error": "No selected file"}), 400
|
||||||
|
|
||||||
|
# 检查文件是不是 Excel 文件
|
||||||
|
if file and allowed_excel(file.filename):
|
||||||
|
try:
|
||||||
|
# 使用 openpyxl 读取文件内容
|
||||||
|
data = parse_table(file, number)
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
result = db_manager.insert_into_class_student(data)
|
||||||
|
print(result)
|
||||||
|
return jsonify({"message": "File successfully processed"}), 200
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
else:
|
||||||
|
return jsonify({"error": "Invalid file type"}), 400
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/get-course-name', methods=['GET'])
|
||||||
|
def get_course_name():
|
||||||
|
period_id, period_name = check_now_time() # 获取当前时间段信息
|
||||||
|
print(f"period_id: {period_id}, period_name: {period_name}")
|
||||||
|
|
||||||
|
# 如果当前不在任何时间段内
|
||||||
|
if period_id is None:
|
||||||
|
return jsonify({
|
||||||
|
'msg': period_name, # 返回不在课程时间段内的消息
|
||||||
|
'data': None
|
||||||
|
})
|
||||||
|
|
||||||
|
# 获取当前用户编号
|
||||||
|
number = session.get('number')
|
||||||
|
if not number:
|
||||||
|
return jsonify({"msg": "用户未登录或编号不可用", "data": None})
|
||||||
|
|
||||||
|
# 获取今天是星期几
|
||||||
|
now = datetime.now()
|
||||||
|
day_of_week = now.weekday() + 1
|
||||||
|
|
||||||
|
# 如果是周末
|
||||||
|
if not (1 <= day_of_week <= 5):
|
||||||
|
return jsonify({"msg": "周末没有课程", "data": None})
|
||||||
|
|
||||||
|
# 如果是工作日,获取课程信息
|
||||||
|
print(f"day of week: {day_of_week}")
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
data = db_manager.get_course_name(number, day_of_week, period_id)
|
||||||
|
print(data)
|
||||||
|
# 返回课程信息
|
||||||
|
return jsonify({
|
||||||
|
'msg': 'ok',
|
||||||
|
'data': data
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/student-sign-in', methods=['POST'])
|
||||||
|
def student_sign_in():
|
||||||
|
number = session.get("number")
|
||||||
|
course_name = request.form.get('course_name')
|
||||||
|
course_id = request.form.get('course_id')
|
||||||
|
now = datetime.now()
|
||||||
|
date = now.strftime("%Y年%m月%d日%H:%M")
|
||||||
|
status = "出勤"
|
||||||
|
print(f"number: {number},course_name: {course_name},course_id: {course_id}")
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
result = db_manager.update_sign_in_info(number, course_id, course_name, date, status)
|
||||||
|
|
||||||
|
if result == 1:
|
||||||
|
return jsonify({"msg": "ok", "data": "签到成功!"})
|
||||||
|
else:
|
||||||
|
return jsonify({"msg": "fail", "data": "签到失败!"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/get-today-courses')
|
||||||
|
def student_get_today_courses():
|
||||||
|
number = session.get('number')
|
||||||
|
if not number:
|
||||||
|
return jsonify({"msg": "用户未登录或编号不可用", "data": None})
|
||||||
|
|
||||||
|
# 获取今天是星期几
|
||||||
|
now = datetime.now() # 获取当前时间
|
||||||
|
day_of_week = now.weekday() + 1
|
||||||
|
|
||||||
|
# 如果是周末
|
||||||
|
if not (1 <= day_of_week <= 5):
|
||||||
|
return jsonify({"msg": "周末没有课程", "data": None})
|
||||||
|
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
data = db_manager.student_get_today_courses(number, day_of_week)
|
||||||
|
|
||||||
|
# 返回课程信息
|
||||||
|
return jsonify({
|
||||||
|
'msg': 'ok',
|
||||||
|
'data': data
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/api/teacher-sign-in', methods=['POST'])
|
||||||
|
def teacher_sign_in():
|
||||||
|
teacher_number = session.get('number')
|
||||||
|
course_id = request.form['course_id']
|
||||||
|
course_name = request.form['course_name']
|
||||||
|
class_name = request.form['class_name']
|
||||||
|
major_id = request.form['major_id']
|
||||||
|
|
||||||
|
now = datetime.now()
|
||||||
|
date = now.strftime("%Y年%m月%d日%H:%M")
|
||||||
|
status = "出勤"
|
||||||
|
print(f"course_id: {course_id},course_name: {course_name},class_name: {class_name},major_id: {major_id}")
|
||||||
|
|
||||||
|
db_manager = DatabaseManager()
|
||||||
|
data = db_manager.teacher_sign_in(course_id, course_name, class_name, major_id, date, status, teacher_number)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True)
|
||||||
|
|||||||
25
config.py
25
config.py
@@ -0,0 +1,25 @@
|
|||||||
|
# config.py
|
||||||
|
import pymysql
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
# 设置session SECRET_KEY
|
||||||
|
SECRET_KEY = 'sUNiJ7QPulxrbmZD'
|
||||||
|
|
||||||
|
# 配置日志
|
||||||
|
LOGGING_CONFIG = {
|
||||||
|
'level': logging.INFO,
|
||||||
|
'format': '%(asctime)s - %(levelname)s - [%(filename)s:%(funcName)s] - %(message)s'
|
||||||
|
}
|
||||||
|
# 配置文件下载路径
|
||||||
|
FILE_PATH = os.path.join(os.getcwd(), 'files')
|
||||||
|
|
||||||
|
# 数据库连接配置
|
||||||
|
DB_CONFIG = {
|
||||||
|
'host': '42.193.20.110',
|
||||||
|
'port': 8006, # 注意端口是数字,不是字符串
|
||||||
|
'user': 'test',
|
||||||
|
'password': 'X7gq9lbxqpDGbyCi',
|
||||||
|
'database': 'test_db',
|
||||||
|
'charset': 'utf8mb4',
|
||||||
|
'cursorclass': pymysql.cursors.DictCursor
|
||||||
|
}
|
||||||
22
db/connection.py
Normal file
22
db/connection.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import pymysql
|
||||||
|
from dbutils.pooled_db import PooledDB
|
||||||
|
from config import DB_CONFIG
|
||||||
|
|
||||||
|
class MySQLPool:
|
||||||
|
def __init__(self):
|
||||||
|
# 初始化时建立数据库连接池
|
||||||
|
self.pool = PooledDB(
|
||||||
|
creator=pymysql, # 使用链接数据库的模块
|
||||||
|
maxconnections=6, # 连接池允许的最大连接数
|
||||||
|
mincached=2, # 初始化时,连接池中至少创建的空闲的连接
|
||||||
|
maxcached=5, # 连接池中最多闲置的连接
|
||||||
|
blocking=True, # 连接池中如果没有可用连接后是否阻塞等待
|
||||||
|
maxusage=None, # 一个连接最多被重复使用的次数,None表示无限制
|
||||||
|
setsession=[], # 开始会话前执行的命令列表
|
||||||
|
ping=0,
|
||||||
|
**DB_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_connection(self):
|
||||||
|
# 从连接池中获取一个连接
|
||||||
|
return self.pool.connection()
|
||||||
299
db/database_manager.py
Normal file
299
db/database_manager.py
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
from utils.time_utils import check_now_time
|
||||||
|
from utils.time_utils import get_time_by_ids
|
||||||
|
from db.connection import MySQLPool
|
||||||
|
import bcrypt
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseManager:
|
||||||
|
def __init__(self):
|
||||||
|
# 使用MySQLPool初始化数据库连接池
|
||||||
|
self.pool = MySQLPool()
|
||||||
|
|
||||||
|
def fetch(self, query, params=None):
|
||||||
|
conn = self.pool.get_connection()
|
||||||
|
try:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(query, params or ())
|
||||||
|
result = cursor.fetchall()
|
||||||
|
return result
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def execute(self, query, params=None):
|
||||||
|
conn = self.pool.get_connection()
|
||||||
|
try:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(query, params or ())
|
||||||
|
conn.commit()
|
||||||
|
return cursor.rowcount
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def executemany(self, query, params_list):
|
||||||
|
conn = self.pool.get_connection()
|
||||||
|
try:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.executemany(query, params_list) # 使用executemany代替execute
|
||||||
|
conn.commit() # 提交事务
|
||||||
|
return cursor.rowcount # 返回受影响的行数
|
||||||
|
except Exception as e:
|
||||||
|
# 可以在这里添加错误处理逻辑,例如打印错误日志、回滚事务等
|
||||||
|
print("An error occurred: ", e)
|
||||||
|
conn.rollback()
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def user_exists(self, phone_number):
|
||||||
|
sql = "SELECT 1 FROM user WHERE number=%s LIMIT 1"
|
||||||
|
result = self.fetch(sql, (phone_number,))
|
||||||
|
return len(result) > 0
|
||||||
|
|
||||||
|
def insert_user(self, user):
|
||||||
|
sql = """
|
||||||
|
INSERT INTO user (name, number, password, status)
|
||||||
|
VALUES (%s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
data = (user.name, user.number, user.password, user.status)
|
||||||
|
# print(data)
|
||||||
|
return self.execute(sql, data)
|
||||||
|
|
||||||
|
def valid_login(self, number, password_attempt):
|
||||||
|
# SQL查询获取用户的哈希密码,身份和状态
|
||||||
|
sql = "SELECT password, status,name FROM user WHERE number=%s LIMIT 1"
|
||||||
|
result = self.fetch(sql, (number,))
|
||||||
|
print(result)
|
||||||
|
if result:
|
||||||
|
stored_hash = result[0]['password'] # 假设结果是密码字段
|
||||||
|
status = result[0]['status'] # 用户状态
|
||||||
|
name = result[0]['name']
|
||||||
|
|
||||||
|
# 使用bcrypt进行密码验证
|
||||||
|
if bcrypt.checkpw(password_attempt.encode('utf-8'), stored_hash.encode('utf-8')):
|
||||||
|
# 密码匹配,返回登录成功,身份和状态
|
||||||
|
return {'valid': True, 'status': status, 'name': name}
|
||||||
|
|
||||||
|
# 密码不匹配或用户不存在,返回登录失败
|
||||||
|
return {'valid': False, 'status': True}
|
||||||
|
|
||||||
|
def get_menu(self, role):
|
||||||
|
sql = "SELECT menu_name,path FROM menu_items WHERE role=%s ORDER BY `order`"
|
||||||
|
result = self.fetch(sql, (role,))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_all_courses(self):
|
||||||
|
sql = "SELECT course_name, course_code, credits, description FROM course"
|
||||||
|
result = self.fetch(sql)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_current_teacher_courses(self, teacher_number):
|
||||||
|
# 使用INNER JOIN连接teacher_class_course表和course表
|
||||||
|
sql = """
|
||||||
|
SELECT
|
||||||
|
c.course_id,
|
||||||
|
c.course_name,
|
||||||
|
c.course_code,
|
||||||
|
c.credits,
|
||||||
|
tcc.class_name,
|
||||||
|
m.major,
|
||||||
|
m.major_id
|
||||||
|
FROM
|
||||||
|
teacher_class_course tcc
|
||||||
|
JOIN
|
||||||
|
course c ON tcc.course_id = c.course_id
|
||||||
|
JOIN
|
||||||
|
major m ON tcc.major_id = m.major_id
|
||||||
|
WHERE
|
||||||
|
tcc.teacher_number = %s;
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 执行查询并返回结果
|
||||||
|
class_student_sql = """SELECT course_id, course_name,class_name,major,major_id,major FROM class_student where teacher_number = %s;"""
|
||||||
|
result1 = self.fetch(sql, (teacher_number,))
|
||||||
|
result2 = self.fetch(class_student_sql, (teacher_number))
|
||||||
|
|
||||||
|
data = []
|
||||||
|
# 遍历第一个结果集
|
||||||
|
if result1:
|
||||||
|
for row in result1:
|
||||||
|
data.append({
|
||||||
|
"course_id": row['course_id'],
|
||||||
|
"course_name": row['course_name'],
|
||||||
|
"class_name": row['class_name'],
|
||||||
|
"major_id": row['major_id'],
|
||||||
|
"major": row['major']
|
||||||
|
})
|
||||||
|
|
||||||
|
# 遍历第二个结果集
|
||||||
|
if result2:
|
||||||
|
for row in result2:
|
||||||
|
data.append({
|
||||||
|
"course_id": row['course_id'],
|
||||||
|
"course_name": row['course_name'],
|
||||||
|
"class_name": row['class_name'],
|
||||||
|
"major_id": row['major_id'],
|
||||||
|
"major": row['major']
|
||||||
|
})
|
||||||
|
# 返回包含两个结果集信息的data列表
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_course_type(self):
|
||||||
|
sql = "SELECT course_name, course_type FROM course"
|
||||||
|
result = self.fetch(sql)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_announcement_info(self):
|
||||||
|
sql = "SELECT course_name, course_type FROM course"
|
||||||
|
result = self.fetch(sql)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def insert_teacher(self, teacher):
|
||||||
|
sql = "INSERT INTO teacher (name, user_id,teacher_number) VALUES (%s, %s, %s);"
|
||||||
|
|
||||||
|
data = (teacher.name, teacher.user_id, teacher.teacher_number)
|
||||||
|
print(data)
|
||||||
|
return self.execute(sql, data)
|
||||||
|
|
||||||
|
def query_user_id(self, phone_number):
|
||||||
|
sql = "SELECT user_id from user WHERE number = %s;"
|
||||||
|
data = self.fetch(sql, phone_number)
|
||||||
|
|
||||||
|
return data[0]['user_id']
|
||||||
|
|
||||||
|
def insert_student(self, student):
|
||||||
|
sql = """
|
||||||
|
INSERT INTO student (student_name, student_number, user_id, major_id, class_name)
|
||||||
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
data = (student.student_name, student.student_number, student.user_id, student.major_id, student.class_name)
|
||||||
|
return self.execute(sql, data)
|
||||||
|
|
||||||
|
def get_course_name(self, student_number, day_of_week, period_id):
|
||||||
|
print(f"student_number: {student_number}, day_of_week: {day_of_week}, period_id: {period_id}")
|
||||||
|
# 从student表获取class_name
|
||||||
|
sql_student = "SELECT class_name FROM student WHERE student_number = %s;"
|
||||||
|
class_name = self.fetch(sql_student, (student_number,))[0]['class_name']
|
||||||
|
|
||||||
|
# 使用class_name和day_of_week从schedule表获取course_id
|
||||||
|
sql_schedule = "SELECT course_id FROM schedule WHERE day_of_week = %s AND class_name = %s AND period_id = %s;"
|
||||||
|
course_id = self.fetch(sql_schedule, (day_of_week, class_name, period_id))
|
||||||
|
print(f"course_id: {course_id}")
|
||||||
|
id = course_id[0]['course_id']
|
||||||
|
print(f"id: {id}")
|
||||||
|
# 对于每一个course_id,从course表中查询course_name
|
||||||
|
sql_course = "SELECT course_name FROM course WHERE course_id = %s;"
|
||||||
|
course_name = self.fetch(sql_course, (id,))[0]['course_name']
|
||||||
|
print(f"course_name: {course_name}")
|
||||||
|
data = {"course_name": course_name, "course_id": id}
|
||||||
|
return data
|
||||||
|
|
||||||
|
def update_sign_in_info(self, student_number, course_id, course_name, date, status):
|
||||||
|
sql = """
|
||||||
|
INSERT INTO attendance_record (student_number, course_id, course_name, date, status)
|
||||||
|
VALUES (%s, %s, %s, %s, %s)
|
||||||
|
"""
|
||||||
|
val = (student_number, course_id, course_name, date, status)
|
||||||
|
result = self.execute(sql, val)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_class_courses(self, student_number):
|
||||||
|
# 1. 查询学生的主修专业
|
||||||
|
major_sql = "SELECT major_id FROM student WHERE student_number = %s;"
|
||||||
|
major_result = self.fetch(major_sql, student_number)
|
||||||
|
print(major_result)
|
||||||
|
major_id = major_result[0]['major_id']
|
||||||
|
|
||||||
|
# 2. 查询专业的所有课程ID
|
||||||
|
course_ids_sql = "SELECT course_id FROM major_course WHERE major_id = %s;"
|
||||||
|
courses_result = self.fetch(course_ids_sql, major_id)
|
||||||
|
course_ids = [course['course_id'] for course in courses_result]
|
||||||
|
|
||||||
|
# 3. 对每个课程ID查询课程详细信息
|
||||||
|
courses_data = []
|
||||||
|
for course_id in course_ids:
|
||||||
|
course_sql = "SELECT course_name, course_code, credits, description FROM course WHERE course_id = %s;"
|
||||||
|
result = self.fetch(course_sql, course_id)
|
||||||
|
courses_data.append(result[0])
|
||||||
|
return courses_data # 返回课程详细信息列表
|
||||||
|
|
||||||
|
def student_get_today_courses(self, student_number, day_of_week):
|
||||||
|
# 从student表获取class_name
|
||||||
|
sql_student = "SELECT class_name FROM student WHERE student_number = %s;"
|
||||||
|
class_name = self.fetch(sql_student, (student_number,))[0]['class_name']
|
||||||
|
|
||||||
|
# 使用class_name和day_of_week从schedule表获取course_id
|
||||||
|
sql_schedule = "SELECT course_id,period_id FROM schedule WHERE day_of_week = %s AND class_name = %s;"
|
||||||
|
schedule_results = self.fetch(sql_schedule, (day_of_week, class_name))
|
||||||
|
print(schedule_results)
|
||||||
|
data = []
|
||||||
|
for i in schedule_results:
|
||||||
|
course_id = i['course_id']
|
||||||
|
sql_course = "SELECT course_name FROM course WHERE course_id = %s;"
|
||||||
|
course_name = self.fetch(sql_course, (course_id))[0]['course_name']
|
||||||
|
id = i['period_id']
|
||||||
|
time = get_time_by_ids(id)
|
||||||
|
data.append({"course_name": course_name, "time": time})
|
||||||
|
return data
|
||||||
|
|
||||||
|
def teacher_sign_in(self, course_id, course_name, class_name, major_id, date, status, teacher_number):
|
||||||
|
class_student_sql = "SELECT student_number FROM class_student WHERE teacher_number = %s AND major_id = %s AND class_name = %s"
|
||||||
|
class_student_result = self.fetch(class_student_sql, (teacher_number, major_id, class_name))
|
||||||
|
|
||||||
|
student_sql = "SELECT student_number FROM student WHERE class_name = %s AND major_id = %s;"
|
||||||
|
student_sql_result = self.fetch(student_sql, (class_name, major_id))
|
||||||
|
|
||||||
|
values_list = []
|
||||||
|
# 检查是否有学生编号被返回
|
||||||
|
if student_sql_result:
|
||||||
|
for student in student_sql_result:
|
||||||
|
# 对于每个学生编号,创建一条记录的值元组
|
||||||
|
student_number = student['student_number']
|
||||||
|
values_list.append((student_number, course_id, course_name, date, status))
|
||||||
|
|
||||||
|
if class_student_result:
|
||||||
|
for class_student in class_student_result:
|
||||||
|
# 注意这里变量名已经修改为class_student
|
||||||
|
student_number = class_student['student_number']
|
||||||
|
values_list.append((student_number, course_id, course_name, date, status))
|
||||||
|
|
||||||
|
# 如果没有任何学生编号,返回相应的消息
|
||||||
|
if not values_list:
|
||||||
|
return {"msg": "当前班级专业没有学生"}
|
||||||
|
|
||||||
|
# 构建一次性插入多条记录的SQL语句
|
||||||
|
attendance_sql = "INSERT INTO attendance_record (student_number, course_id, course_name, date, status) VALUES (%s, %s, %s, %s, %s)"
|
||||||
|
|
||||||
|
# 使用executemany一次性插入多条记录
|
||||||
|
result = self.executemany(attendance_sql, values_list)
|
||||||
|
print(f"Inserted rows: {result}")
|
||||||
|
|
||||||
|
return {"msg": "班级签到成功,签到人数:" + str(result)}
|
||||||
|
|
||||||
|
def insert_into_class_student(self, class_students):
|
||||||
|
try:
|
||||||
|
values_list = [
|
||||||
|
(
|
||||||
|
cs['teacher_number'],
|
||||||
|
cs['class_name'],
|
||||||
|
cs['student_name'],
|
||||||
|
cs['student_number'],
|
||||||
|
cs['course_id'],
|
||||||
|
cs['course_name'],
|
||||||
|
cs['major_id'],
|
||||||
|
cs['major']
|
||||||
|
)
|
||||||
|
for cs in class_students
|
||||||
|
]
|
||||||
|
|
||||||
|
sql = """INSERT INTO class_student
|
||||||
|
(teacher_number, class_name, student_name, student_number, course_id, course_name, major_id, major)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)"""
|
||||||
|
result = self.executemany(sql, values_list)
|
||||||
|
return {"inserted_rows": result}
|
||||||
|
except Exception as e:
|
||||||
|
# 这里应根据实际情况记录日志或返回错误信息
|
||||||
|
print(f"An error occurred: {e}")
|
||||||
|
return {"error": str(e)}
|
||||||
BIN
files/template.xlsx
Normal file
BIN
files/template.xlsx
Normal file
Binary file not shown.
16
models/Student.py
Normal file
16
models/Student.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
class Student:
|
||||||
|
def __init__(self, student_name, student_number, user_id, major_id, class_name):
|
||||||
|
self.student_name = student_name
|
||||||
|
self.student_number = student_number
|
||||||
|
self.user_id = user_id
|
||||||
|
self.major_id = major_id
|
||||||
|
self.class_name = class_name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return (f"Student Name: {self.student_name}, "
|
||||||
|
f"User ID: {self.user_id}, "
|
||||||
|
f"Student Number: {self.student_number}, "
|
||||||
|
f"Major ID: {self.major_id}, "
|
||||||
|
f"Class Name: {self.class_name}")
|
||||||
|
|
||||||
|
|
||||||
8
models/Teacher.py
Normal file
8
models/Teacher.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
class Teacher:
|
||||||
|
def __init__(self, name, user_id, teacher_number):
|
||||||
|
self.name = name
|
||||||
|
self.user_id = user_id
|
||||||
|
self.teacher_number = teacher_number
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Teacher: {self.name}, {self.user_id},{self.teacher_number}"
|
||||||
15
models/User.py
Normal file
15
models/User.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import bcrypt
|
||||||
|
|
||||||
|
|
||||||
|
class User:
|
||||||
|
def __init__(self, name, number, password, status):
|
||||||
|
self.name = name # 用户昵称
|
||||||
|
self.number = number # 手机号
|
||||||
|
self.password = self.hash_password(password) # 哈希密码
|
||||||
|
self.status = status # 状态(是否可用)
|
||||||
|
|
||||||
|
def hash_password(self, password):
|
||||||
|
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"User({self.name}, {self.number}, {'Active' if self.status else 'Inactive'})"
|
||||||
0
models/__init__.py
Normal file
0
models/__init__.py
Normal file
286
mysql.sql
Normal file
286
mysql.sql
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
CREATE TABLE menu_items
|
||||||
|
(
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
menu_name VARCHAR(100),
|
||||||
|
path VARCHAR(255),
|
||||||
|
role ENUM('student', 'teacher'),
|
||||||
|
`order` INT
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO menu_items (menu_name, role, path, `order`)
|
||||||
|
VALUES ('课程信息', 'student', '/course-info', 1),
|
||||||
|
('课程签到', 'student', '/attendance', 2),
|
||||||
|
('公告信息', 'student', '/announcement', 3),
|
||||||
|
('签到提醒', 'student', '/attendance-reminder', 4),
|
||||||
|
('课程类别', 'teacher', '/course-category', 1),
|
||||||
|
('课程信息', 'teacher', '/course-info', 2),
|
||||||
|
('课程签到', 'teacher', '/attendance-teacher', 3),
|
||||||
|
('签到提醒', 'teacher', '/attendance-reminder', 4);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE course
|
||||||
|
(
|
||||||
|
course_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
course_name VARCHAR(255) NOT NULL,
|
||||||
|
course_code VARCHAR(50) NOT NULL,
|
||||||
|
course_type ENUM('选修', '必修') NOT NULL,
|
||||||
|
credits INT,
|
||||||
|
description TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO course (course_name, course_code, course_type, credits, description)
|
||||||
|
VALUES ('大学计算机基础', 'CF001', '必修', 3, '介绍计算机基础知识和应用'),
|
||||||
|
('计算机导论', 'ITC002', '必修', 2, '概述计算机科学的基本概念和原理'),
|
||||||
|
('C++程序设计', 'CP001', '必修', 4, '学习C++编程语言及其应用'),
|
||||||
|
('健康教育', 'HE001', '必修', 1, '提升学生的健康意识和生活方式'),
|
||||||
|
('线性代数及其应用', 'LAAA001', '必修', 4, '学习线性代数的基础理论和实际应用'),
|
||||||
|
('高等数学2A', 'AM_2A', '必修', 4, '深化数学知识,包括微积分、矩阵理论等'),
|
||||||
|
('思想道德修养与法律基础', 'MALS001', '必修', 2, '培养学生的道德素质和法律意识'),
|
||||||
|
('体育A', 'PE_A', '必修', 1, '提高学生的身体素质和运动技能'),
|
||||||
|
('英语读写译1A', 'ERWT_1A', '必修', 3, '提升学生的英语阅读、写作和翻译能力'),
|
||||||
|
('英语听力1A', 'EL_1A', '必修', 2, '提高学生的英语听力理解能力'),
|
||||||
|
('英语口语1A', 'ES_1A', '必修', 2, '提升学生的英语口语表达能力'),
|
||||||
|
('法制安全教育', 'LASE001', '必修', 1, '增强学生的法制观念和安全意识'),
|
||||||
|
('大学二外日语A', 'JSFL_A', '选修', 3, '学习基础的日语语法和词汇'),
|
||||||
|
('军事理论', 'MT01', '必修', 2, '了解军事基本理论和国防知识'),
|
||||||
|
('程序设计实践', 'PP02', '必修', 3, '通过实践项目提升编程能力'),
|
||||||
|
('高等数学2B', 'AM_2B', '必修', 4, '进一步深化高等数学知识'),
|
||||||
|
('中国近现代史纲要', 'OFCMH010', '必修', 2, '概述中国近现代历史发展脉络'),
|
||||||
|
('体育B', 'PE_B', '必修', 1, '继续提升学生的身体素质和运动技能'),
|
||||||
|
('英语听力1B', 'EL_1B', '必修', 2, '进一步提高学生的英语听力理解能力'),
|
||||||
|
('英语读写译1B', 'ERWT_1B', '必修', 3, '深化英语阅读、写作和翻译能力'),
|
||||||
|
('英语口语1B', 'ES_1B', '必修', 2, '进一步提升学生的英语口语表达能力'),
|
||||||
|
('大学生心理健康', 'CSMH01', '必修', 2, '关注和提升大学生的心理健康水平'),
|
||||||
|
('大学物理2A', 'UP_2A', '必修', 4, '学习大学物理的基础理论和实验'),
|
||||||
|
('数字逻辑', 'DL002', '必修', 3, '学习数字电路和逻辑设计的基础知识'),
|
||||||
|
('C++面向对象程序设计', 'CPOB02', '必修', 4, '深入学习C++的面向对象特性及其应用'),
|
||||||
|
('创新人才与大学文化', 'IND010', '选修', 2, '探讨创新人才培养和大学文化建设'),
|
||||||
|
('数据结构', 'DS001', '必修', 4, '学习各种数据结构及其算法'),
|
||||||
|
('Java语言程序设计', 'JS012', '必修', 4, '学习Java编程语言及其应用'),
|
||||||
|
('离散数学导论', 'IDTM01', '必修', 3, '介绍离散数学的基础知识和应用'),
|
||||||
|
('计算机网络', 'CN002', '必修', 4, '学习计算机网络的基础理论和协议');
|
||||||
|
|
||||||
|
CREATE TABLE attendance_record
|
||||||
|
(
|
||||||
|
record_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
student_number VARCHAR(20) NOT NULL,
|
||||||
|
course_id INT NOT NULL,
|
||||||
|
course_name VARCHAR(255) NOT NULL,
|
||||||
|
date VARCHAR(255) NOT NULL,
|
||||||
|
status VARCHAR(20)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE time_period
|
||||||
|
(
|
||||||
|
period_id INT AUTO_INCREMENT UNIQUE,
|
||||||
|
period_name VARCHAR(10),
|
||||||
|
start_time TIME,
|
||||||
|
end_time TIME,
|
||||||
|
PRIMARY KEY (period_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO time_period (period_name, start_time, end_time)
|
||||||
|
VALUES ('一、二节', '08:00:00', '09:30:00'),
|
||||||
|
('三、四节', '10:00:00', '11:30:00'),
|
||||||
|
('五、六节', '14:30:00', '16:00:00'),
|
||||||
|
('七、八节', '16:30:00', '18:00:00');
|
||||||
|
|
||||||
|
CREATE TABLE class_student (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
teacher_number VARCHAR(20) NOT NULL,
|
||||||
|
class_name VARCHAR(20) NOT NULL , -- 班级
|
||||||
|
student_name VARCHAR(50) NOT NULL , -- 姓名
|
||||||
|
student_number VARCHAR(20) NOT NULL UNIQUE, -- 学号
|
||||||
|
course_id INT NOT NULL , -- 课程ID
|
||||||
|
course_name VARCHAR(100) NOT NULL , -- 课程名称
|
||||||
|
major_id VARCHAR(20) NOT NULL,
|
||||||
|
major VARCHAR(255) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE user
|
||||||
|
(
|
||||||
|
user_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(50) NOT NULL,
|
||||||
|
number VARCHAR(15) NOT NULL UNIQUE,
|
||||||
|
password VARCHAR(255) NOT NULL,
|
||||||
|
status BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO user (name, number, password, status) VALUES
|
||||||
|
('教师1','G0001','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('教师2','G0002','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('教师3','G0003','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('教师4','G0004','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生1','X202301000001','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生2','X202301000002','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生3','X202301000003','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生4','X202301000004','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生5','X202301000005','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生6','X202301000006','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生7','X202301000007','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生8','X202301000008','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生9','X202301000009','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生10','X2023010000010','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生11','X2023010000011','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE),
|
||||||
|
('学生12','X2023010000012','$2b$12$COT85R.ice41B/ofAra2ZewTe1En3ZhF6CBKOv2WScTcy.jQAhEVO',TRUE);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE major
|
||||||
|
(
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
major_id VARCHAR(20) NOT NULL UNIQUE,
|
||||||
|
major VARCHAR(255)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO major(major_id,major)
|
||||||
|
values ('000', '计算机科学与技术'),
|
||||||
|
('001', '电子工程'),
|
||||||
|
('002', '经济学'),
|
||||||
|
('003', '生物学');
|
||||||
|
|
||||||
|
CREATE TABLE student
|
||||||
|
(
|
||||||
|
student_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
user_id INT NOT NULL,
|
||||||
|
student_number VARCHAR(20) NOT NULL UNIQUE,
|
||||||
|
student_name VARCHAR(255) NOT NULL,
|
||||||
|
class_name VARCHAR(20) NOT NULL,
|
||||||
|
major_id VARCHAR(20) NOT NULL,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES user (user_id),
|
||||||
|
FOREIGN KEY (major_id) REFERENCES major (major_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO student (user_id, student_number, student_name, class_name,major_id)
|
||||||
|
VALUES
|
||||||
|
(5,'X202301000001','学生1','2023级01班','000'),
|
||||||
|
(6,'X202301000002','学生2','2023级01班','000'),
|
||||||
|
(7,'X202301000003','学生3','2023级01班','000'),
|
||||||
|
(8,'X202301000004','学生4','2023级01班','000'),
|
||||||
|
(9,'X202302000005','学生5','2023级02班','000'),
|
||||||
|
(10,'X202302000006','学生6','2023级02班','000'),
|
||||||
|
(11,'X202302000007','学生7','2023级02班','000'),
|
||||||
|
(12,'X202302000008','学生8','2023级02班','000'),
|
||||||
|
(13,'X202303000009','学生9','2023级03班','001'),
|
||||||
|
(14,'X202303000010','学生10','2023级03班','001'),
|
||||||
|
(15,'X202303000011','学生11','2023级03班','001'),
|
||||||
|
(16,'X202303000012','学生12','2023级03班','001');
|
||||||
|
|
||||||
|
CREATE TABLE teacher
|
||||||
|
(
|
||||||
|
teacher_id INT AUTO_INCREMENT,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
user_id INT,
|
||||||
|
teacher_number VARCHAR(20) NOT NULL UNIQUE,
|
||||||
|
PRIMARY KEY (teacher_id),
|
||||||
|
FOREIGN KEY (user_id) REFERENCES user (user_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO teacher (name, user_id,teacher_number)
|
||||||
|
VALUES ('教师1', 1,'G0001'),
|
||||||
|
('教师2', 2,'G0002'),
|
||||||
|
('教师3', 3,'G0003'),
|
||||||
|
('教师4', 4,'G0004');
|
||||||
|
|
||||||
|
CREATE TABLE major_course
|
||||||
|
(
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
major_id VARCHAR(20) NOT NULL,
|
||||||
|
course_id INT NOT NULL,
|
||||||
|
FOREIGN KEY (major_id) REFERENCES major (major_id),
|
||||||
|
FOREIGN KEY (course_id) REFERENCES course (course_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO major_course (major_id, course_id) VALUES
|
||||||
|
('000',1),
|
||||||
|
('000',2),
|
||||||
|
('000',3),
|
||||||
|
('000',4),
|
||||||
|
('000',5),
|
||||||
|
('000',6),
|
||||||
|
('000',7),
|
||||||
|
('000',8);
|
||||||
|
|
||||||
|
CREATE TABLE teacher_class_course
|
||||||
|
(
|
||||||
|
id INT AUTO_INCREMENT,
|
||||||
|
teacher_number VARCHAR(20) NOT NULL,
|
||||||
|
course_id INT NOT NULL,
|
||||||
|
class_name VARCHAR(20) NOT NULL,
|
||||||
|
major_id VARCHAR(20) NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY (teacher_number) REFERENCES teacher (teacher_number),
|
||||||
|
FOREIGN KEY (course_id) REFERENCES course (course_id),
|
||||||
|
FOREIGN KEY (major_id) REFERENCES major (major_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO teacher_class_course(teacher_number, course_id, class_name,major_id) VALUES
|
||||||
|
('G0001',1,'2023级01班','000'),
|
||||||
|
('G0001',3,'2023级01班','000'),
|
||||||
|
('G0002',2,'2023级02班','000'),
|
||||||
|
('G0002',4,'2023级02班','000'),
|
||||||
|
('G0001',1,'2023级02班','000'),
|
||||||
|
('G0001',3,'2023级02班','000'),
|
||||||
|
('G0002',2,'2023级03班','001'),
|
||||||
|
('G0002',4,'2023级03班','001');
|
||||||
|
|
||||||
|
CREATE TABLE schedule
|
||||||
|
(
|
||||||
|
schedule_id INT AUTO_INCREMENT UNIQUE,
|
||||||
|
day_of_week INT NOT NULL,
|
||||||
|
period_id INT NOT NULL,
|
||||||
|
teacher_number VARCHAR(20) NOT NULL,
|
||||||
|
class_name VARCHAR(20) NOT NULL,
|
||||||
|
course_id INT NOT NULL,
|
||||||
|
PRIMARY KEY (schedule_id),
|
||||||
|
FOREIGN KEY (period_id) REFERENCES time_period (period_id),
|
||||||
|
FOREIGN KEY (teacher_number) REFERENCES teacher (teacher_number),
|
||||||
|
FOREIGN KEY (course_id) REFERENCES course (course_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO schedule (day_of_week, period_id, teacher_number, class_name, course_id) VALUES
|
||||||
|
(1,1,'G0001','2023级01班',1),
|
||||||
|
(1,2,'G0001','2023级01班',3),
|
||||||
|
(1,3,'G0002','2023级01班',2),
|
||||||
|
(1,4,'G0002','2023级01班',4),
|
||||||
|
(2,1,'G0001','2023级01班',1),
|
||||||
|
(2,2,'G0001','2023级01班',3),
|
||||||
|
(2,3,'G0002','2023级01班',2),
|
||||||
|
(2,4,'G0002','2023级01班',4),
|
||||||
|
(3,1,'G0001','2023级01班',1),
|
||||||
|
(3,2,'G0001','2023级01班',3),
|
||||||
|
(3,3,'G0002','2023级01班',2),
|
||||||
|
(3,4,'G0002','2023级01班',4),
|
||||||
|
(4,1,'G0001','2023级01班',1),
|
||||||
|
(4,2,'G0001','2023级01班',3),
|
||||||
|
(4,3,'G0002','2023级01班',2),
|
||||||
|
(4,4,'G0002','2023级01班',4),
|
||||||
|
(5,1,'G0001','2023级01班',1),
|
||||||
|
(5,2,'G0001','2023级01班',3),
|
||||||
|
(5,3,'G0002','2023级01班',2),
|
||||||
|
(5,4,'G0002','2023级01班',4),
|
||||||
|
(1,1,'G0002','2023级03班',2),
|
||||||
|
(1,2,'G0002','2023级03班',4),
|
||||||
|
(1,3,'G0001','2023级02班',1),
|
||||||
|
(1,4,'G0001','2023级02班',3),
|
||||||
|
(2,1,'G0002','2023级02班',2),
|
||||||
|
(2,2,'G0002','2023级02班',4),
|
||||||
|
(2,3,'G0001','2023级02班',1),
|
||||||
|
(2,4,'G0001','2023级02班',3),
|
||||||
|
(3,1,'G0002','2023级03班',2),
|
||||||
|
(3,2,'G0002','2023级03班',4),
|
||||||
|
(3,3,'G0001','2023级02班',1),
|
||||||
|
(3,4,'G0001','2023级02班',3),
|
||||||
|
(4,1,'G0002','2023级02班',2),
|
||||||
|
(4,2,'G0002','2023级02班',4),
|
||||||
|
(4,3,'G0001','2023级02班',1),
|
||||||
|
(4,4,'G0001','2023级02班',3),
|
||||||
|
(5,1,'G0002','2023级03班',2),
|
||||||
|
(5,2,'G0002','2023级03班',4),
|
||||||
|
(5,3,'G0001','2023级02班',1),
|
||||||
|
(5,4,'G0001','2023级02班',3);
|
||||||
|
|
||||||
|
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
|
bcrypt==4.1.2
|
||||||
blinker==1.7.0
|
blinker==1.7.0
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
|
colorama==0.4.6
|
||||||
|
DBUtils==3.0.3
|
||||||
|
et-xmlfile==1.1.0
|
||||||
Flask==3.0.0
|
Flask==3.0.0
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.1.2
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.2
|
||||||
MarkupSafe==2.1.3
|
MarkupSafe==2.1.3
|
||||||
|
openpyxl==3.1.2
|
||||||
|
PyMySQL==1.1.0
|
||||||
Werkzeug==3.0.1
|
Werkzeug==3.0.1
|
||||||
|
|||||||
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
2
utils/allowed_files.py
Normal file
2
utils/allowed_files.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
def allowed_excel(filename):
|
||||||
|
return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'xlsx', 'xls'}
|
||||||
30
utils/parse_table.py
Normal file
30
utils/parse_table.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from openpyxl import load_workbook
|
||||||
|
|
||||||
|
|
||||||
|
def parse_table(file_path, number):
|
||||||
|
# 加载Excel工作簿
|
||||||
|
work_book = load_workbook(file_path)
|
||||||
|
|
||||||
|
# 选择工作簿中的第一个工作表
|
||||||
|
sheet = work_book.active
|
||||||
|
|
||||||
|
# 创建一个列表来存储所有行的数据
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# 从第二行开始遍历(假设第一行是标题行)
|
||||||
|
for row in sheet.iter_rows(min_row=2, values_only=True):
|
||||||
|
# 创建一个字典来存储每行的数据
|
||||||
|
row_data = {
|
||||||
|
"teacher_number": number,
|
||||||
|
"class_name": row[0], # 班级
|
||||||
|
"student_name": row[1], # 姓名
|
||||||
|
"student_number": row[2], # 学号
|
||||||
|
"course_id": row[3], # 课程id
|
||||||
|
"course_name": row[4], # 课程
|
||||||
|
"major_id": row[5], # 专业代码
|
||||||
|
"major": row[6]
|
||||||
|
}
|
||||||
|
data.append(row_data)
|
||||||
|
|
||||||
|
# 返回解析后的数据
|
||||||
|
return data
|
||||||
35
utils/time_utils.py
Normal file
35
utils/time_utils.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
time_periods = {
|
||||||
|
1: {"period_name": "一、二节", "start_time": "08:00:00", "end_time": "09:30:00"},
|
||||||
|
2: {"period_name": "三、四节", "start_time": "10:00:00", "end_time": "11:30:00"},
|
||||||
|
3: {"period_name": "五、六节", "start_time": "14:30:00", "end_time": "16:00:00"},
|
||||||
|
4: {"period_name": "七、八节", "start_time": "16:30:00", "end_time": "18:00:00"}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def check_now_time():
|
||||||
|
# 获取当前时间
|
||||||
|
current_time = datetime.datetime.now().time()
|
||||||
|
|
||||||
|
# 遍历time_periods的每个时间段
|
||||||
|
for period_id, period_info in time_periods.items():
|
||||||
|
start_time = datetime.datetime.strptime(period_info["start_time"], "%H:%M:%S").time()
|
||||||
|
end_time = datetime.datetime.strptime(period_info["end_time"], "%H:%M:%S").time()
|
||||||
|
|
||||||
|
# 检查当前时间是否在时间段内
|
||||||
|
if start_time <= current_time <= end_time:
|
||||||
|
return period_id, period_info["period_name"]
|
||||||
|
|
||||||
|
# 如果当前时间不在任何一个时间段内
|
||||||
|
return None, "当前不在任何课程时间段内"
|
||||||
|
|
||||||
|
|
||||||
|
def get_time_by_ids(id):
|
||||||
|
# 获取对应的时间信息并添加到结果列表
|
||||||
|
period = time_periods.get(id)
|
||||||
|
if period: # 确保id是有效的
|
||||||
|
return period["start_time"][0:5] + "-" + period["end_time"][0:5]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
Reference in New Issue
Block a user