1
0
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-07-01 20:09:23 +00:00
This commit is contained in:
孙悟元 2024-11-17 16:16:16 +08:00
parent 5cc50216e6
commit 8ad86c103f
15 changed files with 1712 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
# Keep environment variables out of version control
.env

136
app.js Normal file
View File

@ -0,0 +1,136 @@
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
const bodyParser = require('body-parser');
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var app = express();
var cors = require("cors");
app.options("*", cors());
app.use(cors())
//全局变量
global.dirname = __dirname;
// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();
// 上传作业数据
app.post("/upload", async (req, res) => {
try {
const date = new Date().toISOString().split("T")[0];
const data = req.body;
// 使用 upsert 来确保每个日期只有一条记录
await prisma.homework.upsert({
where: {
date: date,
},
update: {
data: data,
},
create: {
date: date,
data: data,
},
});
res.json({
status: true,
msg: "上传成功",
});
} catch (error) {
console.error("Upload error:", error);
res.json({
status: false,
msg: "上传失败:" + error.message,
});
}
});
// 下载作业数据
app.get("/download", async (req, res) => {
try {
const date = req.query.date;
const homework = await prisma.homework.findFirst({
where: {
date: date,
},
});
if (!homework) {
throw new Error("该日期未上传数据");
}
res.json({
status: true,
msg: "下载成功",
...homework.data,
});
} catch (error) {
console.error("Download error:", error);
res.json({
status: false,
msg: "下载失败:" + error.message,
});
}
});
app.get("/config", async (req, res) => {
res.json({
"//": "学生名字列表(推荐按学号顺序排列)除最后一项外,每个学生姓名后面必须加一个逗号",
"//": "如果不需要“出勤”功能,请把下面“:”后面的“[]”中的内容删除即可",
studentList: ["张三", "李四", "王五", "赵六", "钱七"],
"//": "作业框排版(前面的中括号中的科目显示在分割线左侧,后面在右侧)除每个中括号中的最后一项外,每个科目后面必须加一个逗号",
homeworkArrange: [
["语文", "数学", "英语"],
["物理", "化学", "生物"],
["政治", "历史", "地理"],
],
"//": "这里需填入部署ServerAPI的服务器url",
"//": "端口默认17312无需改动(除非更改Python代码)",
"//": "127.0.0.1仅适用于本地测试,实际部署请使用公网/内网ip地址或域名",
url: "http://localhost:3030",
});
});
app.get("/test", async (req, res) => {
res.render("test.ejs");
})
app.use("/", indexRouter);
app.use("/users", usersRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render("error");
});
module.exports = app;

90
bin/www Normal file
View File

@ -0,0 +1,90 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('serverexpress:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3030');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}

24
package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "serverexpress",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www",
"prisma": "prisma db pull && prisma generate"
},
"dependencies": {
"@prisma/client": "5.22.0",
"body-parser": "^1.20.3",
"cookie-parser": "~1.4.4",
"cors": "^2.8.5",
"debug": "~2.6.9",
"ejs": "^3.1.10",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"jade": "~1.11.0",
"morgan": "~1.9.1"
},
"devDependencies": {
"prisma": "5.22.0"
}
}

1251
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

15
prisma/schema.prisma Normal file
View File

@ -0,0 +1,15 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model homework {
date String @id @db.VarChar(10)
data Json
createdAt DateTime @default(now())
updatedAt DateTime @default(now())
}

View File

@ -0,0 +1,8 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}

9
routes/index.js Normal file
View File

@ -0,0 +1,9 @@
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;

9
routes/users.js Normal file
View File

@ -0,0 +1,9 @@
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;

3
views/error.ejs Normal file
View File

@ -0,0 +1,3 @@
<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>

2
views/footer.ejs Normal file
View File

@ -0,0 +1,2 @@
</body>
</html>

10
views/hander.ejs Normal file
View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><%= title %></title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>

11
views/index.ejs Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
</body>
</html>

131
views/test.ejs Normal file
View File

@ -0,0 +1,131 @@
<!-- views/index.ejs -->
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>作业板</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.homework-card {
border: 1px solid #ddd;
padding: 15px;
margin: 10px;
border-radius: 5px;
cursor: pointer;
}
.homework-row {
display: flex;
flex-wrap: wrap;
}
.attendance-area {
border: 1px solid #ddd;
padding: 15px;
margin: 10px;
border-radius: 5px;
}
.student-checkbox {
margin: 5px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.student-checkbox input {
margin-right: 10px;
}
pre {
white-space: pre-wrap;
margin: 0;
font-family: inherit;
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- 作业区域 -->
<div class="col-md-8">
<h1>作业 <small class="text-muted"><%= date %></small></h1>
<div id="homeworkContainer">
<% config.homeworkArrange.forEach(function(row) { %>
<div class="homework-row">
<% row.forEach(function(subject) { %>
<div class="col">
<div class="homework-card" onclick="openHomeworkModal('<%= subject %>')">
<h3><%= subject %></h3>
<pre><%= homeworkData[subject]?.content || '' %></pre>
</div>
</div>
<% }); %>
</div>
<% }); %>
</div>
</div>
<!-- 考勤区域 -->
<div class="col-md-4">
<div class="attendance-area">
<h2>出勤</h2>
<p>应到: <%= studentList.length %> 人</p>
<p>实到: <%= studentList.length - selectedStudent.size %> 人</p>
<p>请假: <%= selectedStudent.size %> 人</p>
<form action="/updateAttendance" method="POST">
<input type="hidden" name="date" value="<%= date %>">
<% studentList.forEach(function(student, index) { %>
<div class="student-checkbox">
<label>
<input type="checkbox"
name="selectedStudents"
value="<%= index %>"
<%= selectedStudent.has(index) ? 'checked' : '' %>>
<%= `${index + 1}. ${student}` %>
</label>
</div>
<% }); %>
<button type="submit" class="btn btn-primary mt-3">保存考勤</button>
</form>
</div>
</div>
</div>
</div>
<!-- 作业编辑模态框 -->
<div class="modal fade" id="homeworkModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form action="/updateHomework" method="POST">
<input type="hidden" name="date" value="<%= date %>">
<input type="hidden" name="subject" id="modalSubject">
<textarea class="form-control" name="content" rows="5"></textarea>
<button type="submit" class="btn btn-primary mt-3">保存</button>
</form>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 最小化的JavaScript代码
function openHomeworkModal(subject) {
const modal = $('#homeworkModal');
modal.find('.modal-title').text(subject + '作业');
modal.find('#modalSubject').val(subject);
modal.find('textarea').val($(`#homeworkContainer .homework-card:contains('${subject}') pre`).text());
new bootstrap.Modal(modal).show();
}
</script>
</body>
</html>

10
views/users.ejs Normal file
View File

@ -0,0 +1,10 @@
<%- include('header.html') -%>
<h1>Users</h1>
<ul id="users">
<% users.forEach(function(user){ %>
<li><%= user.name %> &lt;<%= user.email %>&gt;</li>
<% }) %>
</ul>
<%- include('footer.html') -%>