学生管理系统(Nodejs)
一、项目介绍
①使用nodejs+bootstrap开发
②对文件进行合理的模块化
③实现基本的增删改查功能
二、思路
①处理模块,处理模块,配置开发静态资源,配置模块引擎
②路由设计,提取路由模块
③单独的文件用于封装一些方法:查找学生数据,保存学生数据,更新学生数据,删除学生数据
④单独的路由模块实现具体功能:通过路由接收请求,调用数据操作API处理数据,发送操作结果给客户端
三、功能实现
①目录
②npm安装的包
③代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>管理系统</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"> </head> <body> <nav class="navbar navbar-expand navbar-dark bg-dark fixed-top"> <a class="navbar-brand" href="#">学生管理系统</a> </nav> <main class="container mt-5"> <h1 class="alert-heading">学生管理 <a class="btn btn-link btn-sm" href="/students/new">添加学生</a> </h1> <table class="table table-hover"> <thead> <tr> <th>ID</th> <th>姓名</th> <th>性别</th> <th>年龄</th> <th>爱好</th> <th class="text-center" width="140">操作</th> </tr> </thead> <tbody> {{each students}} <tr> <td>{{$value.id}}</td> <td>{{$value.name}}</td> <td>{{$value.gender}}</td> <td>{{$value.age}}</td> <td>{{$value.hobbies}}</td> <td> <a href="/students/edit?id={{$value.id}}">编辑</a> <a href="/students/delete?id={{$value.id}}">删除</a> </td> </tr> {{/each}} </tbody> </table> <ul class="pagination justify-content-center"> <li class="page-item"><a class="page-link" href="#">«</a></li> <li class="page-item"><a class="page-link" href="#">1</a></li> <li class="page-item"><a class="page-link" href="#">2</a></li> <li class="page-item"><a class="page-link" href="#">3</a></li> <li class="page-item"><a class="page-link" href="#">»</a></li> </ul> </main> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加学生</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"> </head> <body> <nav class="navbar navbar-expand navbar-dark bg-dark fixed-top"> <a href="#" class="navbar-brand">学生管理系统</a> </nav> <main class="container mt-5"> <h1 class="alert-heading">添加学生</h1> <form action="/students/new" method="post" autocomplete="off"> <div class="form-group"> <label for="name">姓名</label> <input type="text" name="name" id="name" class="form-control"> </div> <div class="form-group"> <label for="gender" class="radio-inline">性别</label><br> <input type="radio" name="gender" value="0" id="gender">男 <input type="radio" name="gender" value="1" id="gender">女 </div> <div class="form-group"> <label for="age">年龄</label> <input type="number" name="age" id="age" class="form-control"> </div> <div class="form-group"> <label for="hobbies">爱好</label> <input type="text" name="hobbies" id="hobbies" class="form-control"> </div> <button class="btn btn-primary">保存</button> </form> </main> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加学生</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"> </head> <body> <nav class="navbar navbar-expand navbar-dark bg-dark fixed-top"> <a href="#" class="navbar-brand">学生管理系统</a> </nav> <main class="container mt-5"> <h1 class="alert-heading">添加学生</h1> <form action="/students/edit" method="post" autocomplete="off"> <div class="form-group"> <label for="id"></label> <input type="hidden" name="id" id="id" class="form-control" value="{{student.id}}"> </div> <div class="form-group"> <label for="name">姓名</label> <input type="text" name="name" id="name" class="form-control" value="{{student.name}}" > </div> <div class="form-group"> <label for="gender" class="radio-inline">性别</label><br> <input type="radio" name="gender" value="0" id="gender">男 <input type="radio" name="gender" value="1" id="gender">女 </div> <div class="form-group"> <label for="age">年龄</label> <input type="number" name="age" id="age" class="form-control" value="{{student.age}}"> </div> <div class="form-group"> <label for="hobbies">爱好</label> <input type="text" name="hobbies" id="hobbies" class="form-control" value="{{student.hobbies}}"> </div> <button class="btn btn-primary">保存</button> </form> </main> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{ "students": [{ "id": 1, "name": "赵一", "gender": 0, "age": 18, "hobbies": "足球" }, { "id": 2, "name": "孙二", "gender": 1, "age": 20, "hobbies": "篮球" }, { "id": 3, "name": "张三", "gender": 0, "age": 30, "hobbies": "彩票" }, { "id": 4, "name": "李四", "gender": 1, "age": 25, "hobbies": "代码" }, { "id": 5, "name": "王五", "gender": 0, "age": 10, "hobbies": "游戏" }] }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * app.js入口模块: * 1.创建服务, * 2.服务相关配置:模板引擎,body-parser配置,静态服务资源,挂载路由 * 3.监听端口启动服务 */ //npm安装并加载express var express=require('express'); var app=express(); // 加载路由模块 var router=require('./router'); // npm安装并加载body-parser var bodyParser=require('body-parser'); //公开node_modules和public目录 app.use('/node_modules/',express.static('./node_modules/')); app.use('/public/',express.static('./public/')); // npm安装art-template和express-art-template并配置express-art-template(需要在挂载路由之前) app.engine('html',require('express-art-template')); // 配置body-parser(需要在挂载路由之前) app.use(bodyParser.urlencoded({extended:false})); app.use(bodyParser.json()); // 挂载路由 app.use(router); // 绑定端口 app.listen(3000,function(){ console.log('server is running at port 3000...') });
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * router.js路由模块: * 处理路由,根据不同的请求方法或者请求路径设置具体的返回数据 */ // 加载相关模块 var fs=require('fs'); var express=require('express'); var Student=require('./student'); // 创建一个路由容器 var router=express.Router(); /** 路由设计: * 请求方法 ==== 请求路径 ==== 参数 =====备注 * GET ==== /students ==== 参数无 ====渲染首页 * GET ==== /students/new ===== 参数无 ======渲染添加学生页面 * POST ==== /students/new ===== 参数name,age,gender,hobbies ======添加学生页面提交表单 * GET ==== /students/edit ===== 参数id ======渲染编辑页面 * POST ==== /students/edit ===== 参数id,name,age,gender,hobbies ======编辑学生页面表单提交 * GET ==== /students/delete ===== 参数id ======处理删除请求 */ // 处理路由:把路由都挂载到路由容器 router.get('/students',function(req,res){ Student.find(function(err,students){ if(err){ return res.status(500).send('server error') } res.render('index.html',{ students:students }) }); }); router.get('/students/new',function(req,res){ res.render('new.html') }); router.post('/students/new',function(req,res){ // 获取表单数据-->处理(保存到db.json)-->发送响应 Student.save(req.body,function(err){ if(err){ return res.status(500).send('server error') } res.redirect('/students') }) }); router.get('/students/edit',function(req,res){ Student.findById(parseInt(req.query.id),function(err,student){ if(err){ return res.status(500).send('server error') } res.render('edit.html',{ student:student }); }); }); router.post('/students/edit',function(req,res){ Student.updateById(req.body,function(err){ if(err){ return res.status(500).send('server error') } res.redirect('/students') }) }); router.get('/students/delete',function(req,res){ Student.deleateById(req.query.id,function(err){ if(err){ return res.status(500).send('server error') } res.redirect('/students') }); }); // 把路由导出 module.exports=router;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * student.js模块: * 用于数据操作,只处理数据,不关心业务 */ // 加载相关模块 var fs=require('fs'); var dbPath='./db.json'; // 获取所有学生数据列表 exports.find=function(callback){ // callback有两个参数:第一个是err,第二个是data fs.readFile(dbPath,'utf8',function(err,data){ if(err){ return(callback(err)) } callback(null,JSON.parse(data).students) }); }; // 根据id获取学生信息对象 exports.findById=function(id,callback){ fs.readFile(dbPath,'utf8',function(err,data){ if(err){ return callback(err) } var students=JSON.parse(data).students; var ret=students.find(function(item){ return item.id===parseInt(id) }); callback(null,ret) }); } // 添加保存学生数据列表 exports.save=function(student,callback){ fs.readFile(dbPath,'utf8',function(err,data){ if(err){ return callback(err) } var students=JSON.parse(data).students // 处理id唯一的,不重复 student.id=students[students.length-1].id+1 // 保存到数组 students.push(student) // 转为字符串 var fileDate=JSON.stringify({students:students}) // 保存到文件 fs.writeFile(dbPath,fileDate,function(err){ if(err){ return callback(err) } callback(null) }) }); }; // 更新学生数据列表 exports.updateById=function(student,callback){ fs.readFile(dbPath,'utf8',function(err,data){ if(err){ return callback(err) } var students=JSON.parse(data).students; student.id=parseInt(student.id); // find 是ecmascript6的方法 var stu=students.find(function(item){ return item.id===student.id }); // 遍历拷贝对象 for(var key in student){ stu[key]=student[key] } // 把对象数据转为字符串 var fileDate=JSON.stringify({ students:students }); // 保存到文件 fs.writeFile(dbPath,fileDate,function(err){ if(err){ return callback(err) } callback(null) }); }); }; // 删除学生数据 exports.deleateById = function(id,callback){ fs.readFile(dbPath,'utf8',function(err,data){ if(err){ return callback(err) } var students=JSON.parse(data).students; var deleteId=students.findIndex(function(item){ return item.id===parseInt(id) }); students.splice(deleteId,1); var fileDate=JSON.stringify({students:students}); fs.writeFile(dbPath,fileDate,function(err){ if(err){ return callback(err) } callback(null) }); }) }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{ "name": "Code", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "art-template": "^4.12.2", "body-parser": "^1.18.3", "bootstrap": "^4.1.1", "express": "^4.16.3", "express-art-template": "^1.0.1" } }
③效果展示
【转载文章务必保留出处和署名,谢谢!】