本文上篇请 点击阅读
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 = url
if(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 技术,当然掌握了这两门工具,也会提升你的能力。另外,前后端分离项目,总体上会增加一些项目的代码量以及测试工作量。