9.CRUD 系统设计

2019-04-08  本文已影响0人  璎珞纨澜

初始化项目与安装插件

本次设计的CRUD项目开发选用 express 框架;模板引擎选择 art-template;网页 css 样式选择 bootstrap; 获取表单 POST 请求体的API:body-parser。


1.初始化package.json 2.安装express 3.安装art-template 4.安装bootstrap 5.安装body-parser

路由设计

请求方法 请求路径 get参数 post参数 作用
GET /student 渲染首页
GET /student/new 渲染添加学生页面
POST /student/new name、age、gender、major 处理添加学生请求
GET /student/edit id 渲染编辑页面
POST /students/edit id、name、age、gender、major 处理编辑请求
GET /student/delete id 处理删除请求

提取路由模块

路由模块

var express = require('express')

// 1. 创建一个路由容器
var router = express.router()

// 2. 把路由都挂载到 router 路由容器中
router.get('/students', function (req, res) {
})
router.get('/students/new', function (req, res) {
})
router.post('/students/new', function (req, res) {
})
router.get('/student/edit', function (req, res) {
})
router.post('/student/edit', function (req, res) {
})
router.get('/students/delete', function (req, res) {
})

// 3. 把 router 导出 
module.exports = router

app.js

var router = require('./router')

// 挂载路由
app.use(router)

设计 html 页面

在 bootstrap 官网找个css模板仿照着写:
bootstrap官网dashboard模板
bootstrap表单

从文件中读取数据

为了将数据持久化,我们设计将数据存到 json 文件中,通过读取 json 文件得到学生对象,进而渲染整个学生列表页面。简单的代码示例如下:
db.json

{
    "students":[
        {"id":1, "name": "张三", "gender": 0, "age": 20, "major": "英语"},
        {"id":2, "name": "李四", "gender": 0, "age": 20, "major": "英语"},
        {"id":3, "name": "小红", "gender": 1, "age": 20, "major": "数学"},
        {"id":4, "name": "哈哈", "gender": 1, "age": 20, "major": "英语"},
        {"id":5, "name": "嘻嘻", "gender": 0, "age": 20, "major": "数学"},
        {"id":6, "name": "小明", "gender": 0, "age": 20, "major": "英语"}
    ]
}

app.js

var express = require('express')
var fs = require('fs')

var app = express()

app.use('/node_modules', express.static('node_modules'))
app.use('/public', express.static('public'))

app.engine('html',require('express-art-template'))

app.get('/students', function (req, res){
    // readFile的第二个参数是可选的, 传入 utf8 就是把读取到的文件直接按照 utf8编码转换
    // 除了这样来转换之外,也可以通过 data.toString 的方式
    fs.readFile('./db.json', 'utf8', function (err, data){
        if (err) {
            return res.status(500).send('Server error')
        }
        //从文件读取到的数据一定是字符串,所以一定要手动转成对象才能进行渲染
        var students = JSON.parse(data).students
        res.render('new.html', {
            students: students
        })
    })
})
app.listen(3000, function () {
    console.log('running 3000...')
})

设计操作数据的 API 文件模块

// 封装API,函数功能为延时1s,函数参数为回调函数
function delay(callback) {
  setTimeout(function () {
    var data = 'hello'
    callback(data)
  }, 1000)
}
// 调用API,传入的参数(回调函数)是用户自定义的函数,比如这里打印log
delay(function (data) {
  console.log(data)
})

代码

思路大体是这样,详细内容见Code:
app.js

var express = require('express')
var router = require('./router')
var bodyParser = require('body-parser')

var app = express()

app.use('/node_modules', express.static('node_modules'))
app.use('/public', express.static('public'))

app.engine('html',require('express-art-template'))

app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

app.use(router)

app.listen(3000, function () {
    console.log('running 3000...')
})

router.js

var fs = require('fs')
var crud = require('./crud')

var express = require('express')
var router = express.Router()

router.get('/students', function (req, res) {
    crud.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) {
    crud.save(req.body, function (err) {
        if(err) {
            return res.status(500).send('Server error.')
        }
        res.redirect('/students')
    })
})

router.get('/students/edit', function (req, res) {
    crud.findById(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) {
    crud.updateById(req.body, function (err) {
        if(err) {
            return res.status(500).send('Server error.')
        }
        res.redirect('/students')
    })
})

router.get('/students/delete', function (req, res) {
    crud.deleteById(req.query.id, function (err) {
        if(err) {
            return res.status(500).send('Server error.')
        }
        res.redirect('/students')
    })
})

module.exports = router

crud.js


var fs = require('fs')

var dbPath = './db.json'

/**
 * 获取学生列表
 * @param  {Function} callback 回调函数
 */
exports.find = function (callback) {
    fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
            return callback(err)
        }
        callback(null, JSON.parse(data).students)
    })
}

/**
 * 根据学号获取学生信息对象
 * @param  {Number} id 学生学号
 * @param  {Function} callback 回调函数 
 */
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)
    })
}

/**
 * 添加保存学生
 * @param  {Object} student 学生对象
 * @param  {Function} callback 回调函数
 */
exports.save = function (student, callback) {
    fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
            return callback(err)
        }
        var students = JSON.parse(data).students

        student.id = students[students.length - 1].id + 1

        students.push(student)

        var fileData = JSON.stringify({
            students: students
        })

        fs.writeFile(dbPath, fileData, function (err) {
            if (err) {
                return callback(err)
            }
            callback(null)
        })
    })
}

/**
 * 更新学生
 * @param  {Object} student 学生对象
 * @param  {Function} callback 回调函数
 */
exports.updateById = function (student, callback) {
        fs.readFile(dbPath, 'utf8', function (err, data) {
        if (err) {
            return callback(err)
        }
        var students = JSON.parse(data).students

        // 注意:这里记得把 id 统一转换为数字类型
        student.id = parseInt(student.id)
        // EcmaScript 6 中的一个数组方法:find
        // 需要接收一个函数作为参数
        // 当某个遍历项符合 item.id === student.id 条件的时候,find 会终止遍历,同时返回遍历项
        var stu = students.find(function (item) {
            return item.id === student.id
        })
        for (var key in student) {
            stu[key] = student[key]
        }
        var fileData = JSON.stringify({
            students: students
        })
        fs.writeFile(dbPath, fileData, function (err) {
            if (err) {
                return callback(err)
            }
            callback(null)
        })
    })
}

/**
 * 删除学生
 * @param  {Number} id 学生学号
 * @param  {Function} callback 回调函数 
 */
exports.deleteById = 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 fileData = JSON.stringify({
            students: students
        })
        
        fs.writeFile(dbPath, fileData, function (err) {
            if(err) {
                return callback(err)
            }
            callback(null)
        })
    })
}

index.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="../../favicon.ico">

    <title>Dashboard Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="/public/cssdashboard.css" rel="stylesheet">

  </head>

  <body>
    <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
      <h2 class="sub-header">学生名单</h2>
      <a class="btn btn-success" href="/students/new">添加学生</a>
      
      <div class="table-responsive">
        <table class="table table-striped">
          <thead>
            <tr>
              <th>学号</th>
              <th>姓名</th>
              <th>性别</th>
              <th>年龄</th>
              <th>专业</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.major }}</td>
              <td>
                <a href="/students/edit?id={{ $value.id }}">编辑</a>
                <a href="/students/delete?id={{ $value.id }}">删除</a>
              </td>
            </tr>
            {{ /each }}
          </tbody>
        </table>
      </div>
    </div>
  </body>
</html>

new.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="../../favicon.ico">

    <title>Dashboard Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="/public/cssdashboard.css" rel="stylesheet">

  </head>

  <body>
    <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
      <h2 class="sub-header">添加学生</h2>
      <form action="/students/new" method="post">
        <div class="form-group">
          <label for="">姓名</label>
          <input class="form-control" type="text" id="" name="name" required minlength="2" maxlength="10">
        </div>
        <div class="form-group">
          <label for="">性别</label>
          <div class="radio">
            <label>
              <input type="radio" name="gender" id="optionsRadios1" value="0" checked> 男
            </label>
          </div>
          <div class="radio">
            <label>
              <input type="radio" name="gender" id="optionsRadios2" value="1"> 女
            </label>
          </div>
        </div>
        <div class="form-group">
          <label for="">年龄</label>
          <input class="form-control" type="number" id="" name="age" required min="1" max="150">
        </div>
        <div class="form-group">
          <label for="">专业</label>
          <input class="form-control" type="text" id="" name="major">
        </div>
        <button type="submit" class="btn btn-success">Submit</button>
      </form>
    </div>
  </body>
</html>

edit.html

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href="../../favicon.ico">

    <title>Dashboard Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href="/public/cssdashboard.css" rel="stylesheet">

  </head>

  <body>
    <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
      <h2 class="sub-header">编辑学生</h2>
      <form action="/students/edit" method="post">
        <input type="hidden" name="id" value="{{ student.id }}">
        <div class="form-group">
          <label for="">姓名</label>
          <input class="form-control" type="text" id="" name="name" required minlength="2" maxlength="10" value="{{ student.name }}">
        </div>
        <div class="form-group">
          <label for="">性别</label>
          <div class="radio">
            <label>
              <input type="radio" name="gender" id="optionsRadios1" value="0" checked="true"> 男
            </label>
          </div>
          <div class="radio">
            <label>
              <input type="radio" name="gender" id="optionsRadios2" value="1"> 女
            </label>
          </div>
        </div>
        <div class="form-group">
          <label for="">年龄</label>
          <input class="form-control" type="number" id="" name="age" required min="1" max="150" value="{{ student.age }}">
        </div>
        <div class="form-group">
          <label for="">专业</label>
          <input class="form-control" type="text" id="" name="major" value="{{ student.major }}">
        </div>
        <button type="submit" class="btn btn-success">Submit</button>
      </form>
    </div>
  </body>
</html>

总结

上一篇下一篇

猜你喜欢

热点阅读