本文上篇请 点击阅读
1. 需求说明
本文以学生信息查询功能为例,采用前后端分离架构,后端提供RESTFul 接口,前端代码用Vue.js + Bottstrap实现。
1.1 本例要求提供如下查询功能:
列表查询、单条查询
添加学生信息
更改学生信息
删除学生信息
1.2 按REST接口指导原则, RESTFul 风格API 设计如下
在开始之前,推荐阅读 REST接口基本原理
操作 | 请求类型 | 资源请求url | 请求数据 |
---|---|---|---|
列表查询 | GET | http://127.0.0.1:8000/student/ | 无 |
单条查询 | GET | http://127.0.0.1:8000/student/1/ | 无 |
添加记录 | POST | http://127.0.0.1:8000/student/2/ | {‘name’:‘Jack’, ‘no’:‘A001’,…} |
更改记录 | PUT | http://127.0.0.1:8000/student/2/ | {‘name’:‘Jack’, ‘no’:‘B001’,…} |
删除记录 | DELETE | http://127.0.0.1:8000/student/2/ | 无 |
上述接口已通过django-rest-framework 实现.
2. 前端设计
技术栈:
- Vue.js 动态数据更新
- Bootstrap5 负责渲染
- Axios 负责接口通讯
界面设计:
- 以表格方式显示多条数据;
- 每行记录提供修改、删除按钮
- 页面提供添加按钮
- 页面提供查询功能
- 各功能在单页面完成。
实际页面测试:
显示所有记录
查询单条数据
添加数据
修改数据,单击”修改“按钮后,弹出修改栏,更新后单击 ”提交“按钮
删除,直接点击删除按钮即可
3. 完整代码
所有代码均放在1个文件中, vue_student.html
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Vue 测试实例 - Vue router(runoob.com)</title><link href="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.staticfile.org/twitter-bootstrap/5.1.1/js/bootstrap.bundle.min.js"></script><script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script><script src="https://cdn.staticfile.org/vue-router/2.7.0/vue-router.min.js"></script><script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body><div class="container mt-2"><div class="row my-2 mx-2 "><div class="col-md-10 p-3"><h2 class="text-center">学生信息查询</h2><div id='app'><div class="container mt-3"><div class="row"><div class="col-md-6"> <input type='number' v-model="sid" placeholder='请输入学生id'><button v-on:click="searchInfo" class="btn btn-primary" >查询</button></div><div class="col-md-6 d-flex flex-row-reverse"><button class="btn btn-info" v-on:click="addReq">添加学生</button></div></div></div><div class="row my-2 border border-1 border-primary shadow-sm py-2 mx-2" v-if="isShowDataDiv"><div class="form-group row"><label for="stdid" class="col-md-1 col-form-label" >ID</label><div class="col-md-2"><input type="text" class="form-control" v-model="studentInfo.id" id="stdid" readonly="true"></div><label for="stdname" class="col-md-1 col-form-label">姓名</label><div class="col-md-2"><input type="text" class="form-control" v-model="studentInfo.name" id="stdname"></div><label for="stdno" class="col-md-1 col-form-label">学号</label><div class="col-md-2"><input type="text" class="form-control" v-model="studentInfo.no" id='stdno' ></div><div class='col-md-3 mt-2'><span>性别: </span><input type="radio" id="male" value=0 v-model="studentInfo.gender"><label for="runoob" >男</label><input type="radio" id="female" value=1 v-model="studentInfo.gender"><label for="google">女</label></div></div><div class="form-group row"><label for="stdage" class="col-md-1 col-form-label">年龄</label><div class="col-md-2"><input type="number" class="form-control" v-model="studentInfo.age" id="stdage"></div><label for="stdclass" class="col-md-1 col-form-label">班级</label><div class="col-md-2"><input type="text" class="form-control" v-model="studentInfo.class_name" id="stdclass"></div><label for="stdscore" class="col-md-1 col-form-label">成绩</label><div class="col-md-2"><input type="number" class="form-control" v-model="studentInfo.score" id="stdscore"></div><div class="col-md-3 d-flex flex-row-reverse" v-if="isShowUpgradeBtn"><button class="btn btn-outline-primary" v-on:click="upgradeConfirm">更新提交</button></div><div class="col-md-3 d-flex flex-row-reverse" v-if="isShowAddBtn"><button class="btn btn-outline-primary" v-on:click="addConfirm">添加提交</button></div></div></div><table class="table table-striped"><thead><tr><td>ID</td><td>姓名</td><td>学号</td><td>性别</td><td>年龄</td><td>班级</td><td>成绩</td><td>操作</td></tr></thead><tbody><tr v-for="(student,index) in studentArray"><td v-text="student.id"></td><td v-text="student.name"></td><td v-text="student.no"></td><td v-text="student.gender"></td><td v-text="student.age"></td><td v-text="student.class_name"></td><td v-text="student.score"></td><td> <button class='btn btn-primary btn-sm' v-on:click="upgradeReq(student.id,index)">修改</button> <button class='btn btn-danger btn-sm' v-on:click="deleteStudent(student.id,index)">删除</button></td></tr></tbody></table></div></div></div> </div><script>const url = "http://127.0.0.1:8000/student/v1/";var array_index; var vm = new Vue({el: '#app',data: {studentInfo: {id: 1,name:'王小乙',no: 'B0001',gender: 0,age: 14,class_name: '初2',score: 80},studentArray: [],sid: 1,isShowDataDiv: false,isShowAddBtn: false,isShowUpgradeBtn: true},methods: {searchInfo: function(){that = this; let url_req = urlif(that.sid > 0){url_req = url+String(that.sid)+'/';} axios.get(url_req).then( function(response){console.log(response.data); that.studentArray=[]if (response.data instanceof Array){ for (var n in response.data){that.studentArray.push(response.data[n]);}}else if (response.data instanceof Object ){that.studentArray.push(response.data);} }).catch(function (error) { // 请求失败处理console.log(error);})},addData: function(){this.studentArray.push(this.studentInfo);},deleteStudent: function(pk,index){that = this;url_req = url+pk+'/';axios.delete(url_req).then(function(response){ console.log(response.status);that.studentArray.splice(index,1)}).catch(function(err){ console.log(err)})},addStudent: function(){window.open("vue_student_add.html");},addReq: function(){that=this;that.studentInfo = {id: 0,name:'',no: '',gender: 0,age: 0,class_name: '2',score: 0}that.isShowDataDiv=true;that.isShowUpgradeBtn=false;that.isShowAddBtn=true;},addConfirm: function(){that=this; if (that.studentInfo.name !='' && that.studentInfo.no!='' ) {axios.post(url,that.studentInfo).then(function(response){console.log(response);alert("添加成功")that.isShowDataDiv=false;that.isShowUpgradeBtn=true;that.isShowAddBtn=false;that.studentArray.push(that.studentInfo) }).catch(function(err){console.log(err);})}else {alert("姓名,学号不能为空")}},upgradeReq: function(pk,index){that = this;that.studentInfo = that.studentArray[index];array_index = index; that.isShowDataDiv=true;that.isShowUpgradeBtn=true;that.isShowAddBtn=false;// alert("update data " + pk)},upgradeConfirm: function(){that = this; url_req = url+that.studentInfo.id+'/';axios.put(url_req,that.studentInfo).then(response=>{console.log(response.status);that.studentArray[array_index]=that.studentInfo; alert("更新成功")that.isShowDataDiv=false;}).catch(function(err){ console.log(err)}) }}});vm.addData()</script>
</body>
</html>
在测试环境下运行,通常会遇到CORS跨域问题,造成Axios发送的请求被阻止,解决办法请参考本人另一篇博文 Django 解决CORS跨域问题的方法.
总结
DRF+Vue.js 或 DRF+React 前后端分离架构的优点与缺点
采用 DRF+Vue.js 或 DRF+React 前后端分离架构的好处是,能够开发出用户体验更佳的前端页面,不过前提是,项目要有前端工程师加入,或者你自己学习掌握Javascript + Vue 技术,当然掌握了这两门工具,也会提升你的能力。另外,前后端分离项目,总体上会增加一些项目的代码量以及测试工作量。