班级管理实例笔记

231 阅读5分钟

服务器搭建

初始化并下载express依赖

npm init -y
npm i express -s
npm i mongo -s

server.js

const express = require('express'),
      app = express(),
      path = require('path')

app.get('/',(req,res) => {
  // 当访问到 / 时重定向到 /classes 路由
  res.redirect('/classes')
})

app.get('/classes',(req,res) => {
  // 访问/classes路由,服务器返回html页面
  res.sendFile(path.resolve(__dirname + '/classes.html'))
})

app.listen(3000,()=> {
  console.log('3000端口被监听了');
})

前端页面搭建

cdn引入 vue、element-ui、axios

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  <title>班级管理系统</title>
</head>
<body>
  <div id="app">
    <h3>班级管理系统</h3>
    <el-table :data="tableData" border style="width: 100%">
      <el-table-column prop="name" label="姓名" width="180">
      </el-table-column>
      <el-table-column prop="age" label="年龄" width="180">
      </el-table-column>
      <el-table-column prop="score" label="分数" width="180">
      </el-table-column>
      <el-table-column prop="class" label="班级" width="180">
      </el-table-column>
      <el-table-column label="操作">
        <template slot-scope="scope">
          <el-button type="primary" size="small" @click="updateRow(scope.row)">编辑</el-button>
          <el-button type="danger" size="small" @click.native.prevent="deleteRow(scope.row)">删除</el-button>
        </template>
      </el-table-column>

    </el-table>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
  <!-- 引入组件库 -->
  <script src="https://unpkg.com/element-ui/lib/index.js"></script>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    new Vue({
      el: "#app",
      data() {
        return {
          tableData: [
            {
              _id: 1,
              name: 'Max',
              age: 18,
              score: 98,
              class: 1
            }
          ]
        }
      },
      methods: {
        deleteRow(row) {
          console.log(row);
        },
        updateRow(row){
          console.log(row);
        }
      }
    })
  </script>
</body>
</html>

编写路由、连接接口 和 封装db模块

每个接口都对服务器进行一次连接,对服务器的压力是特别大的,这时就需要对数据库的操作进行封装

创建db文件夹

数据库配置

dbConfig.js 用来存放配置

// 数据库对应的 url 和 名字
module.exports = {
  url: 'mongodb://max:123456@192.168.31.67:27017',
  dbName: 'classes'
}

封装db模块

index.js 用来配置mongo实例并抛出

const dbConfig = require('./dbConfig'),
      MongoClient = require('mongodb').MongoClient,
      eventEmitter = require('events')
// 引入事件循环模块来创建emiter

class Mongo{
  constructor(dbConfig) {
    this.dbConfig = dbConfig
    // 实例化事件循环模块
    this.emitter = new eventEmitter()
    // 创建客户端
    this.client = new MongoClient(this.dbConfig.url, {useNewUrlParser:true})
    // 建立连接
    this.client.connect(err=> {
      if(err) throw err
      console.log('数据库连接成功');
      // 绑定事件循环触发连接操作
      this.emitter.emit('connect')
    })
  }
  // 声明调用一次的事件,传入事件名和回调函数
  once(eventName,callBack) {
    // 上面触发emit,下面绑定on,once表示触发一次
    this.emitter.once(eventName, callBack)
  }
  // 获取集合的方法
  // 预留集合名字,如果不传入,默认为classes
  col(colName, dbName = this.dbConfig.dbName) {
    // 返回   客户端的     数据库        →      集合
    return this.client.db(dbName).collection(colName)
  }
}
// 抛出Mongo实例并传入配置信息
module.exports = new Mongo(dbConfig)

调用封装模块

server.js 内引入 index.js 抛出的 mongo实例

在路由内链接数据库的集合对象,对数据进行操作

const mongod = require('./db')

app.get('/api/classList', async (req,res) => {
  // 链接数据库 获取集合对象
  const students = mongod.col('students')
  // 调用find()获取所有的数据
  const classList = await students.find().toArray()
  // 通过json方式返回给前端
  res.json({
    ok: 1,
    classList
  })
})

测试数据

tesData.js 模拟前端传入数据,调用mongo实例的once方法连接数据库并写入数据

const mongodb = require('./index.js')

mongodb.onec('connect', async () => {
  const students = mongodb.col('students')
  try {
    // 插入测试数据
    await students.deleteMany()
    await students.insertMany([
      {
        name: '张三',
        age: 20,
        score: 90,
        class: 1
      },
      {
        name: '李四',
        age: 24,
        score: 90,
        class: 4
      },
      {
        name: '王五',
        age: 21,
        score: 96,
        class: 7
      },
      {
        name: '赵六',
        age: 19,
        score: 76,
        class: 3
      },
      {
        name: '吴七',
        age: 43,
        score: 48,
        class: 4
      },
      {
        name: '钱八',
        age: 38,
        score: 79,
        class: 4
      },
      {
        name: '孙九',
        age: 52,
        score: 95,
        class: 2
      },
      {
        name: '周十',
        age: 46,
        score: 49,
        class: 1
      },
    ])
    console.log('测试数据插入成功');
  } catch(err) {
    console.log(err.stack);
  }
})

在server.js内引入模块,保存,就可以完成数据库操作

const testData = require('./db/testData')

Postman测试接口

Postman汉化中文版

打开postman后,新建工作区

image-20211016203131478

创建新的集合

image-20211016203222475

添加一个请求

image-20211016203311011

输入路由信息

image-20211016203437903

点击下方的键,添加提个查询参数,参数会实时反映到路由

image-20211016203611586

分页接口编写

当前列表能显示4条数据,需要获取第二页的内容,路由则写为

http://localhost:3000/api/classList?pageSize=4&pageIndex=2

在后端接口打印 req.query

app.get('/api/classList', async (req,res) => {
  // http://localhost:3000/api/classList?pageSize=4&pageIndex=2
  console.log(req.query);
})

// { pageSize: '4', pageIndex: '2' }

得到的query解析出了这个请求体的对象,接下来获取这两个属性,传入查询请求,跳过的条数,限制条数,然后返回数据

app.get('/api/classList', async (req,res) => {
  // 解构出这两个属性
  let {pageSize, pageIndex} = req.query
  // 强制将传过来的 string数字 转换为 number数字
  pageSize = Number(pageSize)
  pageIndex = Number(pageIndex)
  // 链接数据库 获取集合对象
  const students = mongod.col('students')
  // 调用find()传入查询参数,获取对应数据
  const classList = await students.find().skip((pageIndex-1) * pageSize).limit(pageSize).toArray()
  res.json({
    ok: 1,
    classList
  })
})

到Postman测试一下,获取到了四条数据

{
    "ok": 1,
    "classList": [
        {
            "_id": "616a19d385c36df56876d6fe",
            "name": "吴七",
            "age": 43,
            "score": 48,
            "class": 4
        },
        {
            "_id": "616a19d385c36df56876d6ff",
            "name": "钱八",
            "age": 38,
            "score": 79,
            "class": 4
        },
        {
            "_id": "616a19d385c36df56876d700",
            "name": "孙九",
            "age": 52,
            "score": 95,
            "class": 2
        },
        {
            "_id": "616a19d385c36df56876d701",
            "name": "周十",
            "age": 46,
            "score": 49,
            "class": 1
        }
    ]
}

前端获取数据

声明所需数据的条数和当前索引,传入到axios请求内,将返回值赋值,完成显示

data() {
  return {
    tableData: [],
    pageIndex: 1,
    pageSize: 4,
  }
},
methods:{
  // 同步化异步操作
  async getClassList() {
    // 发起ajax请求
    const res = await axios.get('/api/classList', {
      // 传入参数
      params: {
        pageIndex: this.pageIndex,
        pageSize: this.pageSize
      }
    })
    // 如果返回的状态 ok 等于 1
    if(res.data.ok === 1) {
      // 赋值给表格数据
      this.tableData = res.data.classList
    }
  },
},
created() {
  // 创建组件时即调用方法发起请求
  this.getClassList()
}

添加页码组件

组件 绑定 pageIndex 和 pageSize,声明总数状态等待从服务器获取,

<el-pagination
	@current-change="handleCurrentChange"
	:current-page="pageIndex"
	:page-size="pageSize"
	layout="total, prev, pager, next, jumper"
	:total="total">
</el-pagination>

<script>
  data() {
    return {
      tableData: [],
      pageIndex: 1,
      pageSize: 4,
      total: 0 // 数据总数从数据库获取
    }
  },
  methods: {
    // 当点击页码时更改pageIndex并发起请求
    handleCurrentChange(val) {
      this.pageIndex = val
      this.getClassList()
    }
  }
</script>

请求这里添加total赋值

// 如果返回的状态 ok 等于 1
if(res.data.ok === 1) {
  // 赋值给表格数据
  this.tableData = res.data.classList
  this.total = res.data.total
}

然后是服务器

// 链接数据库 获取集合对象
const students = mongod.col('students')
// 调用find()的count()聚合方法获取数据总数
const total = await students.find().count()
// 调用find()传入查询参数,获取对应数据
const classList = await students.find().skip((pageIndex-1) * pageSize).limit(pageSize).toArray()
res.json({
  ok: 1,
  classList,
  total // 传递给前端
})

刷新页面 数据 和 页码已经正常显示

删除接口编写

server.js

// 从mongodb导出这个方法,用来将前端传来的id字符串转换为mongodb需要的Id对象
const ObjectId = require('mongodb').ObjectId

// 上方加入这条设置,才能通过req.body解析出数据
// 从前端传过来的默认为 x-www-urlencoded 方式进行传输
app.use(express.urlencoded({extended:true}))


// 删除 post 请求
app.post('/api/deleteStudent',async (req, res) => {
  const {_id} = req.body
  const students = mongod.col('students')
  const r = await students.deleteOne({
    _id: ObjectId(_id)
  })
  console.log(r);
  res.json({ok: 1})
})

获取一个学生的_id,到postman进行测试,点击发送,返回 ok: 1

image-20211016234130910

server.js终端打印结果

{ acknowledged: true, deletedCount: 1 }

刷新html页面,张三这条数据已经消失

测试完成没有问题修改api

// 删除 post 请求
app.post('/api/deleteStudent',async (req, res) => {
  const {_id} = req.body
  const students = mongod.col('students')
  const r = await students.deleteOne({
    _id: ObjectId(_id)
  })
  // 判断数据库返回 true 则把res返回给前端
  if(r.acknowledged === true){
    res.json({ok: 1, msg: '删除数据成功'})
  }
})

前端删除操作

添加删除方法并绑定到删除按钮上

deleteRow(row){
  this.$confirm('此操作将永久删除这个学生, 是否继续?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(async () => {
    // 创建查询参数对象,推入参数
    const data = new URLSearchParams()
    data.append('_id', row._id)
    // 当点击确定按钮则发起ajax请求执行删除操作
    const res = await axios.post('/api/deleteStudent', data)
    if(res.data.ok === 1) {
      // 如果服务器返回 1 则调用查询接口重新获取数据并弹出删除成功信息
      this.getClassList()
      this.$message({
        type: 'success',
        message: '删除成功!'
      });
    }
  }).catch(() => {
    this.$message({
      type: 'info',
      message: '已取消删除'
    });          
  });
}