案例:基于接口的图书管理功能
图书相关的操作基于后台接口数据进行操作:
需要调用接口的功能点:
功能 | 请求方式 | 请求路由 |
① 图书列表数据加载 | GET | http://localhost:3000/books |
② 添加图书 | POST | http://localhost:3000/books |
③ 验证图书名称是否存在 | GET | http://localhost:3000/books/book/:name |
④ 编辑图书-根据ID查询图书信息 | GET | http://localhost:3000/books/:id |
⑤ 编辑图书-提交图书信息 | PUT | http://localhost:3000/books/:id |
⑥ 删除图书 | DELETE | http://localhost:3000/books/:id |
接口文档:
基准路径:http://localhost:3000/
1. 获取图书列表数据
- 路径:books
- 请求方式:get
- 请求参数:无
- 响应结果
[{ "id": "4", "name": "红楼梦", "date": 2525609975000 }, { "name": "三国演义", "date": 2525609975000, "id": 5 }, { "name": "水浒传", "date": 2525609975000, "id": 6 }, { "name": "西游记", "date": 2525609975000, "id": 7 }]
2. 添加图书-提交图书信息
- 路径:books
- 请求方式:post
- 请求参数
- name:图书名称
- 响应结果
{ "status": 200 // (200表示成功;500表示失败) }
3. 编辑图书-根据ID查询图书信息
- 路径:books/:id
- 请求方式:get
- 请求参数:无
- 响应结果
{ "name": "西游记", "date": 2525609975000, "id": 7 }
4. 编辑图书-提交图书信息
- 路径:books/:id
- 请求方式:put
- 请求参数
- name:图书名称
- 响应结果
{ "status": 200 // (200表示成功;500表示失败) }
5. 删除图书信息
- 路径:books/:id
- 请求方式:delete
- 请求参数:无
- 响应结果
{ "status": 200 // (200表示成功;500表示失败) }
6. 验证图书名称是否存在
- 路径:books/book/:name
- 请求方式:get
- 请求参数:无
- 响应结果
{ "status": 1 // (1表示存在;2表示不存在) }
初始化项目:npm init -y
安装相应的第三方模块:
npm install express --save
npm install body-parser --save
目录结构如下:
文件之间的关系如下:
使用nodemon index.js命令启动服务器后,在浏览器中输入下面地址即可查看到动态页面:
books.html文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <link rel="stylesheet" type="text/css" href="/css/index.css"> </head> <body> <div id="app"> <div class="grid"> <div> <h1>图书管理</h1> <div class="book"> <div> <label for="id"> 编号: </label> <input type="text" id="id" v-model='id' disabled="false" v-focus> <label for="name"> 名称: </label> <input type="text" id="name" v-model='name'> <button @click='handle' :disabled="submitFlag">提交</button> </div> </div> </div> <div class="total"> <span>图书总数:</span> <span>{{total}}</span> </div> <table> <thead> <tr> <th>编号</th> <th>名称</th> <th>时间</th> <th>操作</th> </tr> </thead> <tbody> <tr :key='item.id' v-for='item in books'> <td>{{item.id}}</td> <td>{{item.name}}</td> <td>{{item.date | format('yyyy-MM-dd hh:mm:ss')}}</td> <td> <a href="" @click.prevent='toEdit(item.id)'>修改</a> <span>|</span> <a href="" @click.prevent='deleteBook(item.id)'>删除</a> </td> </tr> </tbody> </table> </div> </div> <script type="text/javascript" src="js/vue.js"></script> <script type="text/javascript" src="js/axios.js"></script> <script type="text/javascript"> /* 图书管理-添加图书 */ axios.defaults.baseURL = 'http://localhost:3000/'; // 设置响应拦截请求 axios.interceptors.response.use(function(res){ return res.data; }, function(error){ console.log(error) }); // 设置自定义指令v-focus Vue.directive('focus', { inserted: function (el) { el.focus(); } }); // 设置日期过滤器 Vue.filter('format', function(value, arg) { function dateFormat(date, format) { if (typeof date === "string") { var mts = date.match(/(\/Date\((\d+)\)\/)/); if (mts && mts.length >= 3) { date = parseInt(mts[2]); } } date = new Date(date); if (!date || date.toUTCString() == "Invalid Date") { return ""; } var map = { "M": date.getMonth() + 1, //月份 "d": date.getDate(), //日 "h": date.getHours(), //小时 "m": date.getMinutes(), //分 "s": date.getSeconds(), //秒 "q": Math.floor((date.getMonth() + 3) / 3), //季度 "S": date.getMilliseconds() //毫秒 }; format = format.replace(/([yMdhmsqS])+/g, function(all, t) { var v = map[t]; if (v !== undefined) { if (all.length > 1) { v = '0' + v; v = v.substr(v.length - 2); } return v; } else if (t === 'y') { return (date.getFullYear() + '').substr(4 - all.length); } return all; }); return format; } return dateFormat(value, arg); }) var vm = new Vue({ el: '#app', data: { flag: false, submitFlag: false, id: '', name: '', books: [] }, methods: { handle: async function(){ if(this.flag) { // 编辑图书 var ret = await axios.put('books/' + this.id, { name: this.name }); if(ret.status == 200){ // 重新加载列表数据 this.queryData(); } this.flag = false; }else{ // 添加图书 var ret = await axios.post('books', { name: this.name }) if(ret.status == 200) { // 重新加载列表数据 this.queryData(); } } // 清空表单 this.id = ''; this.name = ''; }, toEdit: async function(id){ // flag状态位用于区分编辑和添加操作 this.flag = true; // 根据id查询出对应的图书信息 var ret = await axios.get('books/' + id); this.id = ret.id; this.name = ret.name; }, deleteBook: async function(id){ // 删除图书 var ret = await axios.delete('books/' + id); if(ret.status == 200) { // 重新加载列表数据 this.queryData(); } }, queryData: async function(){ // 调用后台接口获取图书列表数据 // var ret = await axios.get('books'); // this.books = ret.data; this.books = await axios.get('books'); } }, computed: { total: function(){ // 计算图书的总数 return this.books.length; } }, watch: { name: async function(val) { // 验证图书名称是否已经存在 // var flag = this.books.some(function(item){ // return item.name == val; // }); var ret = await axios.get('/books/book/' + this.name); if(ret.status == 1) { // 图书名称存在 this.submitFlag = true; }else{ // 图书名称不存在 this.submitFlag = false; } } }, mounted: function(){ // var that = this; // axios.get('books').then(function(data){ // console.log(data.data) // that.books = data.data; // }) // axios.get('books').then((data)=>{ // console.log(data.data) // this.books = data.data; // }) this.queryData(); } }); </script> </body> </html>
index.js文件:
const express = require('express'); const path = require('path'); const router = require('./router.js'); const bodyParser = require('body-parser'); const app = express(); // 启动静态资源服务 app.use(express.static('public')); // 处理请求参数 app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); // 配置路由 app.use(router); // 监听端口 app.listen(3000,()=>{ console.log('running...'); });
router.js文件:
/* 路由模块 */ const express = require('express'); const router = express.Router(); const service = require('./service.js'); // 查询图书列表 router.get('/books', service.getAllBooks); // 添加图书(提交表单) router.post('/books', service.addBook); // 跳转到编辑图书信息页面 router.get('/books/:id', service.toEditBook); // router.get('/toEditBook',service.toEditBook); // 编辑图书提交表单 router.put('/books/:id', service.editBook); // 删除图书信息 router.delete('/books/:id', service.deleteBook); // 验证图书名称是否存在 router.get('/books/book/:name', service.checkName); module.exports = router;
service.js文件:
const data = require('./data.json'); const path = require('path'); const fs = require('fs'); // 自动生成图书编号(自增) let maxBookCode = ()=>{ let arr = []; data.forEach((item)=>{ arr.push(item.id); }); return Math.max.apply(null,arr); } // 把内存数据写入文件 let writeDataToFile = (res) => { fs.writeFile(path.join(__dirname,'data.json'),JSON.stringify(data,null,4),(err)=>{ if(err){ res.json({ status: 500 }); } res.json({ status: 200 }); }); } // 验证图书名称是否存在 exports.checkName = (req,res) => { let name = req.params.name; let flag = false; data.some(item=>{ if(name == item.name) { flag = true; return true; } }) if(flag) { res.json({ status: 1 }) }else{ res.json({ status: 2 }) } } // 获取图书列表数据 exports.getAllBooks = (req,res) => { res.json(data); } // 添加图书保存数据 exports.addBook = (req,res) => { // 获取表单数据 let info = req.body; let book = {}; for(let key in info){ book[key] = info[key]; } book.date = 2525609975000; book.id = maxBookCode() + 1; data.push(book); // 把内存中的数据写入文件 writeDataToFile(res); } // 跳转编辑图书页面 exports.toEditBook = (req,res) => { let id = req.params.id; let book = {}; data.some((item)=>{ if(id == item.id){ book = item; return true; } }); res.json(book); } // 编辑图书更新数据 exports.editBook = (req,res) => { let info = req.body; info.id = req.params.id; data.some((item)=>{ if(info.id == item.id){ for(let key in info){ item[key] = info[key]; } return true; } }); // 把内存中的数据写入文件 writeDataToFile(res); } // 删除图书信息 exports.deleteBook = (req,res) => { let id = req.params.id; data.some((item,index)=>{ if(id == item.id){ // 删除数组的一项数据 data.splice(index,1); return true; } }); // 把内存中的数据写入文件 writeDataToFile(res); }
data.json数据文件:
[ { "id": "4", "name": "红楼梦", "date": 2525609975000 }, { "name": "三国演义", "date": 2525609975000, "id": 5 }, { "name": "水浒传", "date": 2525609975000, "id": 6 }, { "name": "西游记", "date": 2525609975000, "id": 7 }, { "name": "明朝那些事儿", "date": 2525609975000, "id": "8" } ]