mirror of
https://github.com/ZeroCatDev/ClassworksKV.git
synced 2025-07-01 11:59:22 +00:00
first
This commit is contained in:
parent
5cc50216e6
commit
8ad86c103f
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
.env
|
136
app.js
Normal file
136
app.js
Normal 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
90
bin/www
Normal 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
24
package.json
Normal 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
1251
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
prisma/schema.prisma
Normal file
15
prisma/schema.prisma
Normal 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())
|
||||
}
|
8
public/stylesheets/style.css
Normal file
8
public/stylesheets/style.css
Normal 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
9
routes/index.js
Normal 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
9
routes/users.js
Normal 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
3
views/error.ejs
Normal file
@ -0,0 +1,3 @@
|
||||
<h1><%= message %></h1>
|
||||
<h2><%= error.status %></h2>
|
||||
<pre><%= error.stack %></pre>
|
2
views/footer.ejs
Normal file
2
views/footer.ejs
Normal file
@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
10
views/hander.ejs
Normal file
10
views/hander.ejs
Normal 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
11
views/index.ejs
Normal 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
131
views/test.ejs
Normal 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
10
views/users.ejs
Normal file
@ -0,0 +1,10 @@
|
||||
<%- include('header.html') -%>
|
||||
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><%= user.name %> <<%= user.email %>></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
|
||||
<%- include('footer.html') -%>
|
Loading…
x
Reference in New Issue
Block a user