Fork me on GitHub

Node.js MVC模式+MongoDB实现学员管理系统

目录结构:

项目入口文件

/* Author:张波 */

/* 
  文件说明:
    此文件是本项目的入口文件
    启动这个项目,会先执行本文件中的代码
*/

// 1. 引入模块
const http = require('http');
const fs = require('fs');
const path = require('path');
const urlmodel = require('url');
const template = require('art-template');
// 引入自定义模块
const bindRender = require('./bindRender');
const router = require('./router');

// 创建Web服务器
var server = http.createServer();

// 监听服务器端口
server.listen(4444, ()=>{
  console.log("Node.js Web server running at: http://127.0.0.1:4444");
})

// 监听服务器请求
server.on('request', (req,res)=>{
  bindRender(res);
  
  // 调用路由模块
  router(req,res);
})
app.js

 

拓展了res方法

// 引入需要的模块
const path = require('path');
const template = require('art-template');


// 给res这个响应对象拓展一个方法
function bindRender(res){
  res.render = function(fileName, objData){
    var htmlStr = template(path.join(__dirname, "views/" + fileName + ".html"), objData);
    this.end(htmlStr);
  }
  res.json = function(obj){
    this.end(JSON.stringify(obj))
  }
}

// 向外暴露出去
module.exports = bindRender;
bindRender.js

 

路由模块

const path = require('path');
const urlmodel = require('url');
const fs = require('fs');
// 引入自定义模块
const herosController = require('./herosController');


function router(req, res) {
  var url = urlmodel.parse(req.url, true);
  var pathname = url.pathname;
  var query = url.query;
  // 获取浏览器客户端的请求方式
  var method = req.method;
  // 根据请求的地址进行判断,响应对应的页面
  if (method == "GET" && (pathname == "/" || pathname == "/index")) {
    //调用方法,执行代码
    herosController.showIndexPage(req,res);
  } else if (method == "GET" && pathname == "/add") {
    // 读取add.html页面响应给浏览器
    herosController.showAddPage(req,res);

  } else if (method == "GET" && pathname == "/info") {
    // 读取info.html页面响应给浏览器
    herosController.showInfoPage(req,res);

  } else if (method == "GET" && pathname == "/edit") {
    // 读取edit.html页面响应给浏览器
    herosController.showEditPage(req,res);

  }else if(method == "POST"&&pathname =='/addHero'){
    // 调用控制器中的方法
    herosController.addHero(req,res);

  }else if(method == "GET" &&pathname == "/delHero"){
    // 调用控制器中的删除方法
    herosController.delHeroById(req,res);
    // 加载静态资源
  }else if(method == 'POST' && pathname == '/updateHero'){
    herosController.updateHeroById(req,res);

  }else if (method == "GET" && pathname.startsWith('/node_modules')) {
    herosController.loadStaticResource(req,res);
  } else {
    res.end("404 Not Found!");
  }
}

// 向外暴露router
module.exports = router;
router.js

 

控制器模块

/**
 * 1.此文件是一个控制器模块
 * 2.在此模块当中是专门用来书写业务逻辑的
 * 3.比如说,响应给浏览器主要数据(html页面,静态文件,普通)
 * 4.再比如,接收浏览器传递过来的数据进行处理 
 * 5.总而言之,复杂的业务逻辑要写在这个模块当中
 * 
 * 在前后端的请求交互当中,req,res是最最重要的两个对象,几乎无时无刻都得使用,因此使用的时候,最好是成对出现
 */

 // 1. 引入需要的模块
 const path = require('path')
 const fs = require('fs')
 const url = require('url')
 const querystring = require('querystring');// 这个内置核心模块会将post过来的字符串转换成对象
 const moment = require('moment')
 const herosModel = require('./herosModel')


 // 向外暴露数据
 module.exports = {
     showIndexPage (req,res) {
      herosModel.getAllHeros((err,r)=>{
        // 一定要通过回调函数的方式来获取异步方法中的数据
        if(err) return res.end('404')

        // var arr = JSON.parse(r);
        // 向浏览器渲染数据
        // console.log(r);
        // console.log(typeof r);
        res.render('index', {"data":r}) // 响应给浏览器对应的页面
      })
     },
     showAddPage (req,res) {
       
       res.render('add', {}) 
     },
     showInfoPage: function (req,res) {
       // 1. 将传递过来的路径进行进一步的解析
       var urlObj = url.parse(req.url, true);
      //  获取传过来的ID
       var id = urlObj.query.id;
        // 根据ID获取那一条英雄对象数据
        herosModel.getHeroById(id,(err,r)=>{
          if(err) return res.end('404')
          // 如果查询成功就渲染页面
          res.render('info', r[0])
        })      
     },
     showEditPage: function (req,res) {
      // 1.对请求路径进行解析
      var urlObj = url.parse(req.url, true);
      
      // 获取传递过来的id
      var id = urlObj.query.id;
      herosModel.getHeroById(id,(err,hero)=>{
        if(err) return res.end('404');
        res.render('edit',hero[0]);
      })
     },
     loadStaticResource(req,res){
       // 读取对应的css文件响应给浏览器
       var urlObj = url.parse(req.url,true)
       let pathname = urlObj.pathname
       fs.readFile(path.join(__dirname, pathname), 'utf-8', (err, data) => {
         // if(err) return console.log(err.message);
         if (err) return res.end('not found this file')
         if (pathname.endsWith('css')) {
           // 如果路径是以css结尾,说明请求的是css文件,需要加上响应头,否则不加响应头
           res.writeHeader(200, {
             'Content-Type': 'text/css;charset=utf-8'
           })
         }
         res.end(data)
       })
     },
     addHero(req,res){
       // 1. 接收前端传递过来的数据
       var str = '';// 这个字符中是用来接收post过来的请求体数据
       req.on('data',chunk=>{
         //  给req注册一个data事件,每当前端页面有数据发送来,就会立即触发这个事件
         // chunk块的意思,也就是说数据是分块传递的,一块一块又一块的方式来传递
         //  每发送一点数据,都会触发一下data事件
         str += chunk; // 将接收到的数据累加起来
       })

       req.on('end',()=>{
         // 当end事件被触发的时候,表示数据已经接收完毕
         var hero = querystring.parse(str)      // 把post发送过来的字符串参数转换成对象
        hero.time = moment().format('YYYY-MM-DD hh:mm:ss');
        herosModel.getAllHeros((err,data)=>{
          if(err) return console.log(err.message);

          //var arr = JSON.parse(data); 将读取到的字符串转换成数组对象
          hero.id = Number(data[data.length-1].id) + 1

          // 接下来应该将hero这个数据传递给model模块,让model模块写入dada.json中
          herosModel.addHero(hero,result=>{
            // 如果添加失败,则返回给浏览器一个回执信息
            if (result) return res.end(JSON.stringify({
              "code": 1,
              "msg": "添加失败"
            }))

            // 如果添加成功,也需要返回给浏览器一个回执信息
            res.end(JSON.stringify({
              "code": 0,
              "msg": "添加成功"
            }))
          })
        })
       })
     },
     delHeroById(req,res){
       var urlObj = url.parse(req.url,true)
       // 获取传递过来的ID
       var id = urlObj.query.id;
       
        // 调用model中的方法来根据id删除对应的英雄数据
        herosModel.delHeroById(id,result=>{
          if(result) return res.end(JSON.stringify({
            "code": 1,
            "msg": "删除失败"
          }))
          res.end(JSON.stringify({
            "code": 0,
            "msg": "删除成功"
          }))
        })
     },
     updateHeroById(req,res){
       // 1.接收浏览器端post过来的数据
       var str = '';
       req.on('data', chunk=>{
         // post请求是通过请求体一块一块的发送给服务器的,每次发送一快,就会触发一次data事件,被chunk接收一次
         str += chunk;
       })
       req.on('end', ()=>{
         // 当end事件结束的时候,说明已经全部收到了发送过来的数据
         var hero = querystring.parse(str);  // 将字符串转换成对象
         hero.time = moment().format('YYYY-MM-DD hh:mm:ss')   // 添加一个time属性

        //  2.调用model中的方法实现更新
        herosModel.updateHeroById(hero,result=>{
          if(result) res.end(JSON.stringify({
            "code":1,
            "msg":"更新失败"
          }))
          res.json({
            "code":0,
            "msg":"更新成功"
          })
        })
       })
     }
   }
herosController.js

 

数据库增删改查模块

/* 
  1.此模块是一个model模块
  2.此模块主要是用来操作数据的,比如说数据的CRUD
  3.只能用这个模块来操作数据库中的数据
*/


// 引入对应的模块
const fs = require('fs');
const path = require('path');
// 引入Mongodb模块
const MongoClient = require('mongodb').MongoClient;

//设置数据库地址和端口
var dburl = "mongodb://127.0.0.1:27017/heros";


// 直接向外暴露对应的数据
module.exports = {
  // 查询数据库信息并返回给控制器方法
  getAllHeros(callback) {
    // 连接Database
    MongoClient.connect(dburl, function (err, db) {
      if (err) return console.log("[!]连接数据库失败!")
      console.log("[+]MongoDB连接成功!")

      db.collection("heros").find().toArray(function (err, r) {
        // 回调函数
        if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器
        // 如果读取成功,则将r返回给控制器
        callback(null, r);
        db.close();
      })
    })
  },
  addHero(hero, callback) {
    this.getAllHeros((err, data) => {
      if (err) return callback(err);
      // 因为查询数据库得到的结果就是一个数组对象,所以不需要转换
      data.push(hero);

      // 连接Database
      MongoClient.connect(dburl, function (err, db) {
        console.log(dburl);
        if (err) return console.log("[!]连接数据库失败!")
        console.log("[+]MongoDB连接成功!")

        db.collection("heros").insertOne({
          "id": Number(hero.id),
          "name": hero.name,
          "age": hero.age,
          "phone": hero.phone,
          "address": hero.address,
          "gender": hero.gender,
          "time": hero.time
        }, (function (err, r) {
          // 回调函数
          if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器
          // 如果读取成功,则将r返回给控制器
          callback(null, r);
          db.close();
        }))
      })
    })
  },
  getHeroById(id, callback) {
    // 连接Database
    MongoClient.connect(dburl, function (err, db) {
      if (err) return console.log("[!]连接数据库失败!")
      console.log("[+]MongoDB连接成功!")

      db.collection("heros").find({
        "id": Number(id)
      }).toArray(function (err, r) {
        // 回调函数
        if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器
        // 如果读取成功,则将r返回给控制器
        callback(null, r);
        db.close();
      })
    })
  },
  delHeroById(id, callback) {
    // 连接Database
    MongoClient.connect(dburl, function (err, db) {
      if (err) return console.log("[!]连接数据库失败!")
      console.log("[+]MongoDB连接成功!")

      db.collection("heros").deleteOne({
        "id": Number(id)
      }, function (err, r) {
        // 回调函数
        if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器
        // 如果读取成功,则将r返回给控制器
        callback(null, r);
        db.close();
      })
    })

  },
  updateHeroById(hero, callback) {
    console.log("俺来了...");
      // 找到数据库中存在的数据ID,改一下其他属性即可.
    var id = hero.id;
    console.log(id);
    console.log(typeof id);
    // 连接Database
    MongoClient.connect(dburl, function (err, db) {
      if (err) return console.log("[!]连接数据库失败!")

      db.collection("heros").updateOne({"id": Number(id)}, {$set:{"name":hero.name, "age":hero.age, "phone":hero.phone, "address":hero.address, "gender":hero.gender, "time":hero.time}},function (err, r) {
        // 回调函数
        if (err) return callback(err); // 如果查询失败,就将错误对象返回给给控制器
        // 如果读取成功,则将r返回给控制器
        callback(null, r);
        db.close();
      })
    })
  }
}
herosModel.js

 

页面代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Hero - Admin</title>
  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
</head>

<body>
  <header>
    <div class="page-header container">
      <h1>MongoDB<small> 学员管理系统</small></h1>
    </div>
  </header>
  <div class="container hero-list">
    <a class="btn btn-success pull-right" href="/add">添加学员</a>
    <table class="table table-hover">
      <thead>
        <th>学员编号</th>
        <th>学员名称</th>
        <th>学员性别</th>
        <th>学员年龄</th>
        <th>学员手机号</th>
        <th>创建日期</th>
        <th>操作</th>
      </thead>
      <tbody>
        {{each data  val index}}
        <tr>
          <td>{{val.id}}</td>
          <td>{{val.name}}</td>
          <td>{{val.gender}}</td>
          <td>{{val.age}}</td>
          <td>{{val.phone}}</td>
          <td>{{val.time}}</td>
          <td>
            <a href="/info?id={{val.id}}">查看</a>
            <a href="/edit?id={{val.id}}">编辑</a>
            <a class="delHero" href="javascript:;" data-id="{{val.id}}">删除</a>
          </td>
        </tr>
      {{/each}}
      </tbody>
    </table>
  </div>
  <script src="/node_modules/jquery/dist/jquery.js"></script>
  <script>
    $(".delHero").on('click', function(){
      // 发送ajax请求
      $.ajax({
        type:"get",
        url:"/delHero",
        data:{
          id:$(this).data("id")
        },
        dataType:"json",
        success:function(res){
          if(res.code==0){
            location.reload(true);    //不要用缓存来刷新,要重新发送请求,用最新的数据刷新页面
          }
        }
      })
    })
  </script>
</body>

</html>
index.html

 

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Hero - Admin</title>
  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
</head>

<body>
  <header>
    <div class="page-header container">
      <h1><a href="/">MongoDB</a> <small>学员详细信息</small></h1>
    </div>
  </header>
  <div class="container hero-list">
    <p><strong>Id:</strong>{{id}}</p>
    <p><strong>姓名: </strong>{{name}}</p>
    <p><strong>性别: </strong>{{gender}}</p>
    <p><strong>年龄: </strong>{{age}}</p>
    <p><strong>手机号码: </strong>{{phone}}</p>
    <p><strong>家庭住址: </strong>{{address}}</p>
  </div>
</body>

</html>
info.html

 

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Hero - Admin</title>
  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
</head>

<body>
  <header>
    <div class="page-header container">
      <h1><a href="/">MongoDB</a> <small>修改学员信息</small></h1>
    </div>
  </header>
  <div class="container hero-list">
    <form id="myForm">
      <input type="hidden" name="id" value="{{id}}">
      <div class="form-group">
        <label class="col-sm-2 control-label">学员名称</label>
        <div class="col-sm-10">
          <input type="text" name="name" class="form-control" value="{{name}}">
        </div>
      </div>
      <div class="form-group">
        <label class="col-sm-2 control-label">学员年龄</label>
        <div class="col-sm-10">
          <input type="text" name="age" class="form-control" value="{{age}}">
        </div>
      </div>
      <div class="form-group">
        <label class="col-sm-2 control-label">学员手机号</label>
        <div class="col-sm-10">
          <input type="text" name="phone" class="form-control" value="{{phone}}">
        </div>
      </div>
      <div class="form-group">
        <label class="col-sm-2 control-label">学员住址</label>
        <div class="col-sm-10">
          <input type="text" name="address" class="form-control" value="{{address}}">
        </div>
      </div>
      <div class="form-group">
        <label class="col-sm-2 control-label">性别</label>
        <div class="col-sm-10">
          <label class="radio-inline">
            <input type="radio" value="男" name="gender" {{if gender=='男'}} checked {{/if}}> 男
          </label>
          <label class="radio-inline">
            <input type="radio" value="女" name="gender" {{if gender=='女'}} checked {{/if}}> 女
          </label>
        </div>
      </div>
      <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
          <button type="submit" class="btn btn-default btn-success">保存修改</button>
        </div>
      </div>
    </form>
  </div>
  <script src="/node_modules/jquery/dist/jquery.js"></script>
  <script>
    // 1. 给form标签注册submit事件
    $('#myForm').on('submit',function(event){
      // 2. 阻止默认提交行为  因为我们当前是使用异步ajax的方式来提交数据
      event.preventDefault();

      // 3. 发送ajax请求
      $.ajax({
        type:'post',
        url:'/updateHero',
        data:$(this).serialize(), // 可以一次性获取form标签中具有name属性的input/select标签的值,并拼接成字符串
        dataType:'json', // 将接收到的服务器端的json形式的字符串转换成json对象
        success:function(res){
          console.log(res);
          console.log(typeof res);
          if(res.code==0){
            // 跳转到主页面index.html
            console.log("数据接收成功!")
            location.href='/'
            // 当向服务器端发送请求的时候,/前面的http://127.0.0.1:3000浏览器会自动帮我们给拼上
            // 虽然我们写的是 location.href='/',但是浏览器真正的请求路径是:http://127.0.0.1:3000/
          }
        }
      })
    })
  </script>
</body>

</html>
edit.html

 

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Hero - Admin</title>
  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
</head>

<body>
  <header>
    <div class="page-header container">
      <h1><a href="/">MongoDB</a> <small>添加学员</small></h1>
    </div>
  </header>
  <div class="container hero-list">
    <form id="myForm">
      <div class="form-group">
        <label>学员姓名</label>
        <input type="text" name="name" class="form-control" placeholder="请输入学员名称">
      </div>
      <div class="form-group">
        <label>学员年龄</label>
        <input type="text" name="age" class="form-control" placeholder="请输入学员年龄">
      </div>
      <div class="form-group">
        <label>学员手机号</label>
        <input type="text" name="phone" class="form-control" placeholder="请输入学员手机号">
      </div>
      <div class="form-group">
        <label>学员住址</label>
        <input type="text" name="address" class="form-control" placeholder="请输入学员住址">
      </div>
      <div class="form-group">
        <label>学员性别</label>
        <div class="radio">
          <label>
            <input type="radio" name="gender" value="男" checked></label>
          <label>
            <input type="radio" name="gender" value="女"></label>
        </div>
      </div>
      <button type="submit" class="btn btn-success">点击保存</button>
    </form>
  </div>
  <script src="/node_modules/jquery/dist/jquery.js"></script>
  <script>
    // 给form标签注册submit事件
    $('#myForm').on('submit', function(event){
      // 阻止form标签的默认行为
      event.preventDefault();


    // 发送ajax请求
    $.ajax({
      type:'post',
      url:'/addHero',
      data:$(this).serialize(), // 将form标签中具有name属性的input/select标签的值一次性拼接成'键=值&键=值'的字符交给异步去发送
      dataType:'json',
      success:function(res){
        if(res.code == 0){
          location.href = '/';    // 跳转到首页
        }
      }
    })
  })
  </script>
</body>

</html>
add.html

 

项目效果:

 

 

 

 

posted @ 2019-05-30 09:31  replaceroot  阅读(331)  评论(0编辑  收藏  举报