目录
一、新建项目并初始化
二、安装依赖 axios、elementUI
三、Vue代码
四、进行接口联调
五、后端接口优化
六、web页面功能测试
七、总结
八、展望
九、附录(截止发文时的代码)
先来看看效果,再步入正题
主页
增加
查询
详情
修改
删除
先打一下地基
环境搭建博文:
VUE 3.0项目环境搭建+VS code安装(图文教程)_软件测试李同学的博客-CSDN博客
Flask+mysql接口增删改查博文:Flask+mysql实现增删改查接口开发+测试(图文教程附源码)_软件测试李同学的博客-CSDN博客
注意:本篇文章接着上面的图书项目book增删改查实现
vue 创建新项目 :vue create book
初始化 :vue init webpack book
中间要是提示要安装脚手架:npm i -g @vue/cli-init
,按照提示执行命令即可
安装:npm i -g @vue/cli-init
新建项目成功后,目录结构如下:
axios说白了就是调接口用的
安装axios npm
npm install axios --save
安装element-ui
npm install element-ui --save (yarn add element-ui)
elementUI说白了就是一个组件库,很流行,实战的时候肯定是CV多,有轮子就不用重复写轮子
elementUI的引入
在src下面的main.js下新增如下内容
import Element from 'element-ui'
import "element-ui/lib/theme-chalk/index.css"
Vue.use(Element)
axios的引入
在src下面的main.js下新增如下内容
import axios from 'axios'
Vue.prototype.$axios=axios
组件中,我们就可以通过this.$axios.get()来发起我们的请求了哈
通过命令 npm run dev 启动
以下代码也是轮子,轮子有了,先看看效果
向src->components->HelloWorld.vue文件中加入以下代码,然后保存
{{ msg }}
添加信息
编辑
删除
运行看看效果
继续联调,发现vue文件还有很多需要改的地方
理智分析一波
1、先是先将对应的模板写好,包括输入框(查找操作),增加按钮(增加操作),删除按钮(删除操作),修改按钮(修改操作),表格(显示数据)
模板代码如下:
{{ msg }}
添加图书
*
书名
*
作者
*
阅读状态
详情
编辑
删除
2、制作好模板之后,就得编写下各个方法,包括显示数据方法(getnetwork)增加方法(confirm_add),删除方法(deleteRow),查询方法(check),修改方法(confirm)
显示方法代码如下:
getnetwork() {
// console.log(this.bookinfo.id);
var that = this
this.$axios.post("http://127.0.0.1:5001/query",{
id: "", //这里不传id查所有
})
.then(function(res) {
console.log(res.data)
that.bookInfo = res.data.data
that.bookInfo = that.bookInfo.map(item=>{
item.read_status = String(item.read_status)
return item
}
)
// console.log(this.bookInfo)
}).
catch(function(err) {
console.log(err)
})
}
增加方法(confirm_add)如下:
//5、 增加方法
confirm_add() {// 确定增加按钮
if (this.editObj1.title === '') {
this.$message({
message: '书名不能为空!',
type: 'warning'
});
return
}
if (this.editObj1.author === '') {
this.$message({
message: '作者不能为空!',
type: 'warning'
});
return
}
if (this.editObj1.read_status === '') {
this.$message({
message: '阅读状态不能为空!',
type: 'warning'
});
return
}
var that = this
//网络请求获取数据axios
this.$axios.post("http://127.0.0.1:5001/add", {
title: this.editObj1.title,
author: this.editObj1.author,
read_status: this.editObj1.read_status
}).then(function(res) { //请求成功,方法回调
// this.$message.success('图书添加成功!');
//回调方法不能用this
that.getnetwork()
// if (this.editObj1.read_status == success)
console.log(res.data)
//that.newsList = res.data
}).catch(function(err) { //请求失败
console.log(err)
})
this.addbook_dialogVisible = false;
// Vue.set(this.tableData, this.userIndex, this.editObj);
},
// 给表头增加提示
renderHeader(h,{column}){
const serviceContent= [
h('div',{
slot:"content",
},
"提示:true代表已读,false代表未读"
)
]
return h("div",[
h("span",column.label),
h("el-tooltip",{
props:{
placement:"top"
}
},
[
serviceContent,
h("i",{
class:"el-icon-warning-outline",
style:"color:blue;margin-left:5px;"
})
])
]);
},
},
}
删除方法(deleteRow)如下:
//3、删除方法
deleteRow(id) {
console.log(id)
if (confirm('确定要删除吗') == true) {
var that = this
//网络请求获取数据axios
this.$axios.post("http://127.0.0.1:5001/delete", {
id:id
})
.then(res =>{ //请求成功,方法回调
//回调方法不能this
this.getnetwork()
// getnetwork();
console.log(res.data)
}).catch(function(err) { //请求失败
console.log("失败了" + err)
})
}
},
// 对话框右上角的×按钮
handleClose() {//编辑
this.edit_dialogVisible = false;
},
handleClose1() {//增加
this.addbook_dialogVisible = false;
},
detail_handleClose() {//详情
this.detail_dialogVisible = false;
}
查询方法(check)如下:
//2、查询方法
check() {//确定查询按钮
// console.log(this.bookname)
var that = this
//网络请求获取数据axios
this.$axios.post("http://127.0.0.1:5001/query", {
id: this.id,
}).then(function(res) { //请求成功,方法回调
//回调方法不能用this
console.log(res.data)
console.log(res.data.data)
that.bookInfo = res.data.data
that.bookInfo = that.bookInfo.map(item=>{//布尔类型转字符串
item.read_status = String(item.read_status)
return item
})
// console.log(this.data.id)
}).
catch(function(err) { //请求失败
console.log(err)
})
},
修改方法(confirm) 如下:
//4、修改方法
confirm() { //确认修改
if (this.editObj.title === '') {
this.$message({
message: '书名不能为空!',
type: 'warning'
});
return
}
if (this.editObj.author === '') {
this.$message({
message: '作者不能为空!',
type: 'warning'
});
return
}
if (this.editObj.read_status === '') {
this.$message({
message: '阅读状态不能为空!',
type: 'warning'
});
return
}
var that = this
//网络请求获取数据axios
this.$axios.post("http://127.0.0.1:5001/update", {
id: this.editObj.id,
title: this.editObj.title,
author: this.editObj.author,
read_status: this.editObj.read_status
}).then(function(res) { //请求成功,方法回调
//回调方法不能this
that.getnetwork()
console.log(res.data)
}).catch(function(err) { //请求失败
console.log(err)
})
this.edit_dialogVisible = false;
// Vue.set(this.tableData, this.userIndex, this.editObj);
},
1、传单个id的时候返回的单个数据没在数组里,这里要处理一下
2、删除的方法由DELETE改为POST
3、。。。。还有很多别的,具体看代码
准备测试,看看增删改查效果
增加
删除
修改
查询
查询所有
通过编号查询
其他的测试场景和细节体验这里就不测试了
代码的注释已经写得很清楚了
上一篇博文的代码有一些bug,不知道大家找出来没。这一次做的增删改查前端加入了很多小细节,基本是就是工作中需要测试的细节,如果是自己实现的话,这个会给大家带来很深的印象。
还有对阅读状态这个布尔型字段的处理,有点费功夫的,坑也多
很多校验还没做好,还有很多bug,前期设计不太合理,后续需要优化。
1、更改为书名查询,模糊查询
2、修复增加和修改后页面自动按照id查询的bug。。。等bug
3、增加输入框长度校验、提示校验,目前做的还不够
4、增加操作成功和失败后的提示
5、删除确认框的处理
6、做好分页功能
7、做好封装,接口、方法等
8、。。。。。。
完成以上工作,一个健壮的增删改查就有了,已经满足工作的要求了
1、后端app.py全部代码
# -*- coding: utf-8 -*-
# @Author : Liqiju
# @Time : 2022/5/1 2:45
# @File : app.py
# @Software: PyCharm
import pymysql
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask import make_response,request
from flask_cors import CORS
pymysql.install_as_MySQLdb()
app = Flask(__name__)
# ------------------database----------------------------
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:root@localhost:3306/books'
# 指定数据库文件
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 允许修改跟踪数据库
db = SQLAlchemy(app)
class Books(db.Model):
id = db.Column(db.Integer, primary_key=True, comment='自动递增id,唯一键')
title = db.Column(db.String(80), nullable=False, comment='书名')
author = db.Column(db.String(120), nullable=False, comment='作者')
read_status = db.Column(db.Boolean, comment='阅读状态,0未读,1已读') # bool的True和False是数值1和0
# 增加数据
def insert_data(title, author, read_status):
book = Books(title=title, author=author, read_status=read_status)
db.session.add_all([book])
db.session.commit()
# 查询所有
def select_data_all():
book_list = []
books = Books.query.all()
# 类似于 select * from Books
for s in books:
dic = {}
dic['id'] = s.id
dic['title'] = s.title
dic['author'] = s.author
dic['read_status'] = s.read_status
book_list.append(dic)
return book_list
# 通过id查询
def select_data_by_id(id):
book_list = []
book = Books.query.get(id)
if not book:
return False
dic = {}
dic['id'] = book.id
dic['title'] = book.title
dic['author'] = book.author
dic['read_status'] = book.read_status
book_list.append(dic)
return book_list
# 通过id删除数据
def delete_data(id):
# 类似于 select * from Books where id = id
delete_id = Books.query.get(id)
if not delete_id:
return False
db.session.delete(delete_id)
db.session.commit()
# 提交操作到数据库
# 修改数据
def update_data(id, title='', author='', read_status=''):
book = Books.query.get(id)
if not title == '':
book.title = title
if not author == '':
book.author = author
if not read_status == '':
book.read_status = read_status
db.session.commit()
# 解决浏览器浏览器访问输出乱码问题
app.config['JSON_AS_ASCII'] = False
@app.after_request
def after(resp):
resp = make_response(resp)
resp.headers['Access-Control-Allow-Origin'] = '*' # 允许跨域地址
resp.headers['Access-Control-Allow-Methods'] = '*' # 请求 ‘*’ 就是全部
resp.headers['Access-Control-Allow-Headers'] = 'x-requested-with,content-type' # 头部
resp.headers['Access-Control-Allow-Credentials'] = 'True'
return resp
CORS(app, resources=r'/*', supports_credentials=True)
# 前端通过传参title、author、read_status增加书籍
@app.route('/add', methods=['POST'])
def add():
response_object = {'status': 'success'}
if request.method == 'POST':
post_data = request.get_json()
print('调用add方传过来的参数是', post_data)
book_list = select_data_all()
for i in range(len(book_list)):
title_list = book_list[i]['title']
if post_data.get('title') in title_list:
response_object['message'] = '书名(title)重复!'
response_object["status"]= 'fail'
return response_object
if post_data.get('title') is None:
response_object['message'] = 'title是必传参数!'
response_object["status"]= 'fail'
return response_object
if post_data.get('author') is None:
response_object['message'] = 'author是必传参数!'
response_object["status"]= 'fail'
return response_object
if post_data.get('read_status') is None:
response_object['message'] = 'read_status是必传参数!'
response_object["status"]= 'fail'
return response_object
title = str(post_data.get('title')).strip(),
author = str(post_data.get('author')).strip(),
read_status = int(str(post_data.get('read_status')))# 前端传过来字符串处理为int
if title[0] is None or title[0] is '':
response_object['message'] = 'title不能为空!'
response_object["status"] = 'fail'
return response_object
if author[0] is None or author[0] is '':
response_object['message'] = '作者不能为空!'
response_object["status"] = 'fail'
return response_object
if read_status != 0 and read_status != 1:
response_object['message'] = '阅读状态只能为0和1!'
response_object["status"] = 'fail'
return response_object
insert_data(title=title[0], author=author[0], read_status=read_status)
response_object['message'] = '图书添加成功!'
return response_object
# 前端通过传id删除书籍
@app.route('/delete', methods=['POST']) # 改为post方法
def delete():
response_object = {'status': 'success'}
if request.method == 'POST':
post_data = request.get_json()
print('调用delete方传过来的参数是:', post_data)
if post_data.get('id') is None:
response_object['message'] = 'id是必传参数!'
response_object["status"]= 'fail'
return response_object
id = post_data.get('id')
result = delete_data(id) # 删除方法调用
if result is False:
response_object['message'] = '需要删除的图书不存在!'
response_object["status"] = 'fail'
return response_object
else:
response_object['message'] = '图书被删除!'
return response_object
# 前端通过传参title、author、read_status修改书籍
@app.route('/update', methods=['POST'])
def update():
response_object = {'status': 'success'}
if request.method == 'POST':
post_data = request.get_json()
print('调用update方传过来的参数是', post_data)
if post_data.get('id') is None:
response_object['message'] = 'id是必传参数!'
response_object["status"]= 'fail'
return response_object
if post_data.get('title') is None:
response_object['message'] = 'title是必传参数!'
response_object["status"]= 'fail'
return response_object
if post_data.get('author') is None:
response_object['message'] = 'author是必传参数!'
response_object["status"]= 'fail'
return response_object
if post_data.get('read_status') is None:
response_object['message'] = 'read_status是必传参数!'
response_object["status"]= 'fail'
return response_object
# 查询所有数据
book_list = select_data_all()
# 拼接所有的id到列表
print(book_list)
# 这里的逻辑写错了,有bug,改一下
book_id = []
for i in range(len(book_list)):
book_id.append(book_list[i]['id'])
print('book_id是', book_id)
# 判断书籍id在不在列表内
if post_data.get('id') not in book_id and int(post_data.get('id')) not in book_id: # 这里也有bug,改一下
response_object['message'] = '需要修改的图书id不存在!'
response_object["status"]= 'fail'
return response_object
title = str(post_data.get('title')).strip(),
author = str(post_data.get('author')).strip(),
#print("处理前",post_data.get('read_status'))
read_status = int(post_data.get('read_status')) # 前端传过来字符串处理为int
#print("处理后", read_status)
if title[0] is None or title[0] is '':
response_object['message'] = 'title不能为空!'
response_object["status"] = 'fail'
return response_object
if author[0] is None or author[0] is '':
response_object['message'] = '作者不能为空!'
response_object["status"] = 'fail'
return response_object
if read_status != 0 and read_status != 1:
response_object['message'] = '阅读状态只能为0和1!'
response_object["status"] = 'fail'
return response_object
books_id = post_data.get('id')
title = post_data.get('title')
author = post_data.get('author')
read_status = read_status
# 这里原来的post_data.get('read_status')改为read_status,上面已经处理了
update_data(id=books_id, title=title, author=author, read_status=read_status)
response_object['message'] = '图书已更新!'
return response_object
# 前端通过不传参默认查询所有书籍,传id查询对应书籍
@app.route('/query', methods=['POST'])
def query():
response_object = {'status': 'success'}
if request.method == 'POST':
post_data = request.get_json()
print('调用query方传过来的参数是', post_data)
# if post_data.get('id') is None:
id = str(post_data.get('id')).strip()
if id is None or id is '':
books = select_data_all()
response_object['message'] = '查询所有图书成功!'
response_object['data'] = books
return response_object
# id = str(post_data.get('id')).strip()
# if id is None or id is '':
# response_object['message'] = 'id不能为空!'
# response_object["status"] = 'fail'
# return response_object
book = select_data_by_id(id)
if book is False:
response_object['message'] = '需要查询的图书不存在!'
response_object["status"] = 'fail'
return response_object
else:
response_object['message'] = '图书查询成功!'
response_object['data'] = book
return response_object
if __name__ == '__main__':
# 默认是5000,这里设置5001避免本地冲突。打开debug方便调试
# db.create_all() # 创建表(表创建好后可注释掉)
# insert_data("《水浒传》", "吴承恩", 1) # 利用这个可以添加数据或者直接数据库手动加入
# 注意这个时候没开启校验title唯一性是因为不是前端过来的请求
app.run(debug=True, port=5001)
2、前端HelloWorld.vue全部代码
{{ msg }}
添加图书
*
书名
*
作者
*
阅读状态
详情
编辑
删除
有疑问来评论区或者私信一起学习交流