之前写过一个数据构造工具,当时用的Django+vue,后来又用flask重写了一下后端逻辑

本次打算在现有基础上加点东西:新增一个数据列表,数据列表中展示曾经创建好的数据

拆解下这次要做的功能:

1、每创建一次数据,都把数据写到数据库中;

2、从数据库中查询创建好的数据返给前端;

3、前端构建一个数据列表,把后端数据渲染到列表中;

4、数据列表添加查询功能;

5、数据列表添加分页功能


1、添加查询功能

在页面添加列表查询功能,我需要构造2个查询条件:

【数据类型】,把它做成下拉框形式,筛选对应类型的数据

【创建日期】,通过日期筛选创建日期在所选时间范围内的数据


点【查询】会把对应参数传到请求中,筛选符合条件的结果;

点【重置】会清空查询框输入的条件;


这里要用到element-ui中​Select 选择器​、​Form 表单​ 、​DatePicker 日期选择器


这部分样式代码如下

<el-row>
<el-col :span="24">
<div class="grid-content bg-purple-dark">
<el-form :inline="true" :model="form" size="small" ref="ruleForm" class="demo-form-inline" style="margin-left: 10px">

<el-form-item label="数据类型" prop="class">
<el-select v-model="form.class" placeholder="请选择数据类型" clearable>
<el-option label="电话" value="1"></el-option>
<el-option label="身份证ID" value="2"></el-option>
<el-option label="姓名" value="3"></el-option>
</el-select>
</el-form-item>

<el-form-item label="创建日期" prop="create_date">
<el-date-picker
v-model="form.create_date"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd HH:mm:ss"
:default-time="['00:00:00', '23:59:59']">
</el-date-picker>
</el-form-item>

<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">查询</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</el-col>
</el-row>


对应的js代码

<script>
export default {
data() {
return {
form: {
class: '',
create_date: '',
}
}
},

methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {

let payload = { // 定义请求参数
class_type: this.form.class,
create_date: this.form.create_date
}

console.log("打印查询条件输入的参数payload")
console.log(payload)

console.log("打印日期框选择框填写的日期")
console.log(this.form.create_date)

} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
}
}
</script>


实现效果


 flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql 


代码说明:

1、点击【重置】,能够清空输入框输入的内容

在js代码中创建了2个方法​​submitForm()​​和​​resetForm()​​,分别绑定到【查询】【重置】按钮如果想实现点击【重置】清空内容,需要给表单添加添加​​ref属性​​ 和​​prop属性​ref的值 是调用 submitForm() 和 resetForm() 时传入的值,比如​​ref="form_data"​​,则调用resetForm()时需要传入ref,即​​resetForm('form_data')​​; prop的值 是对应表单组件model的值,比如<el-select>中​​v-model="form.class"​​,所以它对应的​​prop="class"​

flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_02


2、日期控件 DatePicker 的使用配置

日期这块期望实现这样一种效果:选择开始日期-结束日期后,例如2022-01-13~2011-01-15后,接口传参为 2022-01-13 00:00:00~2022-01-15 23:59:59


在element-ui官方文档中,可以找到相关配置参数


使用​​value-format​​指定绑定值的格式,例如​​value-format="yyyy-MM-dd HH:mm:ss"​


使用​​default-time​​ 指定起始日期的时刻与结束日期的时刻,例如​​:default-time="['00:00:00', '23:59:59']"​

flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_03


3、定义请求参数,查看一下前端传的参数的具体值是什么样的

submitForm()方法中先定义了查询接口触发时所需的参数:一个是数据类型,一个是创建日期


flask+vue:创建一个数据列表并实现简单的查询功能(一)_创建日期_04


class_type表示数据类型,create_date表示创建日期

它们分别获取前端传来的参数,打印一下结果


 flask+vue:创建一个数据列表并实现简单的查询功能(一)_创建日期_05


可以看到create_date是一个包含开始日期和结束日期数组,

接下来再看一下参数为空的清空

(1)数据类型、创建日期默认为空时,传的参数如下

create_date的值为​​''​

flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_06


(2)数据类型、创建日期先填写值再重置,传的参数如下

create_date的值为​​['']​

flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_07 

可以看到创建日期默认为空时,传的值为​''​先赋值再重置,传的值为为​​['']​

所以后端处理create_date为空的情况时需要考虑这种情况

2、添加列表

使用​Table 表格​组件添加一个列表展示数据

样式代码

<el-table
:data="tableData"
border
height="350"
style="width: 100%; margin-top: 30px;margin-left: 10px">
<!-- height 表格默认高度,添加height属性,即可实现固定表头的表格

-->
<el-table-column
type="index"
width="50">
</el-table-column>
<el-table-column
prop="date"
label="日期"
width="180"
align="center"> <!--使用align控制对齐方式-->
</el-table-column>
<el-table-column
prop="type"
label="类型"
width="180"
align="center">
</el-table-column>
<el-table-column
prop="value"
label="生成的测试数据"
align="center">
</el-table-column>
</el-table>

上述代码中,在<el-table-column>中,使用 ​​align="center"​​ 控制每列标题的对齐方式,​​:data="tableData"​​,表示往列表中插入的数据


对应js代码

<script>
export default {
data() {
return {
tableData: [{
date: '2022-01-10',
type: '电话号码',
value: '13140845519'
}, {
date: '2022-01-10',
name: '电话号码',
value: '18136773435'
}, {
date: '2022-01-10',
type: '电话号码',
value: '14592741294'
}]
}
}
}
</script>

上述代码中​​tableData​​表示往列表中插入的数据,目前是一些假数据,等下从后端获取到数据后,需要把数据包装成这种格式赋给​​tableData​

flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_08



3、添加分页功能

使用 ​​Pagination 分页​​ 组件给列表进行分页

flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_09


样式代码

<div class="block" style="margin-top: 10px; text-align: right;"> <!--使用text-align控制div右对齐-->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="[5, 10, 20, 30, 50]"
:page-size="5"
layout="total, sizes, prev, pager, next, jumper"
:total="parseInt(count)">
</el-pagination>
</div>


对应js代码

<script>
export default {
data() {
return {
form: {
class: '',
create_date: '',
},
currentPage: 1,
pageSize:5,
count: null,
tableData: null
};
},
methods: {
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.pageSize = val
console.log("打印当前的pageSize")
console.log(this.pageSize)
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {

let payload = { // 定义请求参数
class_type: this.form.class,
create_date: this.form.create_date,
page_num: this.currentPage,
page_size: this.pageSize
}

console.log("打印查询条件输入的参数payload")
console.log(payload)

console.log("打印日期框选择框填写的日期")
console.log(this.form.create_date)

} else {
console.log('error submit!!');
return false;
}
});
}
}
}
</script>


在分页组件中,需要用到几个参数:总数据条数total(共xx条)、每页条数page-size、当前页码current-page


其中总数据条数是根据当前查询条件查询后后端返回的数据总量;

当前页码、每页条数这2个参数需要跟着请求发送,后端根据参数来返回对应查询结果


上述js代码中,在​​data()​​下新增了4个参数:其中​count​用来接收后端返回的数据总量,它的值必须为整数​tableData​用来接收接口返回并处理后的列表数据


其中​currentPage​和​pageSize​,分别表示当前页码和每页条数,等会儿给请求传参时,我们会用到它俩,所以我们用这2个参数接收前端的current-page和page-size

flask+vue:创建一个数据列表并实现简单的查询功能(一)_创建日期_10


我期望达到的效果是当选择每页条数或者切换页码时,这个2个参数能够传给后端实时的数值

这里有2种实现方式,一种是利用.sync 修饰符,一种是利用size-change或者current-change事件


(1)利用.sync 修饰符

html中,给当前页码参数current-page通过.sync 修饰符绑定​​currentPage​​这样当前页码变化时,​​currentPage​​也会获取到最新的值

flask+vue:创建一个数据列表并实现简单的查询功能(一)_创建日期_11


(2)利用size-change事件

上述js代码中,在method()中添加了一个方法​​handleSizeChange()​​方法,它的回调参数就是每页条数


然后在前端组件中用​​@size-change​​绑定这个事件,那么当每页条数发生变化时,就会触发这个事件,回调参数即是当前的每页条数


在​​handleSizeChange()​​中,我把回调参数val的值赋给​​pageSize​​参数,这样​​pageSize​​就能得到最新的每页条数了

flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_12

可以直接把val赋给pageSize:​this.pageSize = val​同时,在组件中仍然要给​​page-size​​赋一个初始值,这样每次刷新页面,当前每页条数就显示这个定义的初始值

flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_13

最后观察​​submitForm()​​方法,我在​​payload对象​​中添加了2个参数​​page_num​​、​​page_size​​,这俩参数其实是我传给后端请求接口中的2个参数


它们分别接收data()中的​​currentPage​​和​​pageSize​​的值

flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_14

在控制台打印下结果,可以看到每次切换当前条数和页码,都能获取到最新的值

 flask+vue:创建一个数据列表并实现简单的查询功能(一)_创建日期_15


4、后端处理

前端代码先写到这里,接下来先在后端把接口定义出来

我们需要定义一个接口来供前端调用,根据前端传参,来返回列表所需的数据

前端会传4个参数:​​class_type​​、​​create_date​​、​​page_num​​、​​page_size​


因为数据创建好后存到了数据库中,所以我们需要从数据库中查出数据返给前端


编写sql时需要考虑到如下几点:


  • 当某个查询条件为空时,sql语句中则不加这个条件;
  • 当处理日期时,需要考虑前端日期组件传来空值的情况(在上面提了一下,前端创建日期如果默认为空时,传的值为''
    ;如果先选择日期再重置,传的值为为['']
    );


  • 日期存在数据库为datetime对象,期望显示在前端时经过格式化,按照"年-月-日"显示;
  • 因为涉及到分页,根据前端请求参数,控制查询第一页数据、第二页数据等以及每页数据条数;

创建一个蓝图,​data_list.py​

# coding: utf-8
"""
author: hmk
detail:
create_time:
"""

from flask import Blueprint
from flask_restful import Api, Resource
from flask import request
from utils.connect_db import MysqlConnect
import time

select_data_bp = Blueprint('select_data', __name__) # 创建一个蓝本
api = Api(select_data_bp) # 使用这个蓝本创建一个Api对象


class SelectData(Resource):

def __init__(self):
self.db = MysqlConnect()

def get(self):
"""列表查询接口"""
class_type = request.args.get("class_type") # 获取前端参数"type"
create_date = request.args.getlist("create_date[]") # 获取前端传来的list格式数据(前端叫做array,数组)
page_num = int(request.args.get("page_num")) # 当前页码
page_size = int(request.args.get("page_size")) # 每页显示数据条数
print("********************")
# print(class_type)
print(create_date)
# print(page_num)
# print(page_size)
# print(type(page_num))

class_type_data = { # 定义一个字典,映射数据类型与类型编号的关系
"1": "电话号码",
"2": "身份证id",
"3": "姓名"
}

sql1 = None
sql2 = None

if class_type == "":
if not create_date or create_date == ['']:
sql1 = "select type_name, value, date_format(create_time, '%Y-%m-%d') from data_list LIMIT {},{}"\
.format((page_num-1)*page_size, page_size)

sql2 = "select count(*) from data_list;"
else:
startDate = create_date[0] # request.args.get("startDate")
endDate = create_date[1] # request.args.get("endDate")
sql1 = "select type_name, value, date_format(create_time, '%Y-%m-%d') from data_list where " \
"create_time between '{}' AND '{}' LIMIT {},{};"\
.format(startDate, endDate, (page_num-1)*page_size, page_size)

sql2 = "select count(*) from data_list where create_time between '{}' AND '{}';"\
.format(startDate, endDate)

elif class_type != "":
if not create_date or create_date == ['']:
sql1 = "select type_name, value, date_format(create_time, '%Y-%m-%d') from data_list " \
"where type_name='{}' LIMIT {},{};"\
.format(class_type_data[class_type], (page_num-1)*page_size, page_size)

sql2 = "select count(*) from data_list where type_name='{}';".format(class_type_data[class_type])

else:
startDate = create_date[0] # request.args.get("startDate")
endDate = create_date[1] # request.args.get("endDate")
sql1 = "select type_name, value, date_format(create_time, '%Y-%m-%d') from data_list " \
"where type_name='{}'" \
"and create_time between '{}' AND '{}' LIMIT {},{};"\
.format(class_type_data[class_type], startDate, endDate, (page_num-1)*page_size, page_size)

sql2 = "select count(*) from data_list " \
"where type_name='{}'" \
"and create_time between '{}' AND '{}';".format(class_type_data[class_type], startDate, endDate)

print("################### 打印sql1 #########################")
print(sql1)

history_data = self.db.select_all(sql1)
print("################### 打印查询到的所有数据 #########################")
print(history_data)

print("################### 打印sql2 #########################")
print(sql2)

count = self.db.select_one(sql2)[0]
print("################### 打印查询到的数据总条数 #########################")
print(count)

self.db.close()
data = {
"code": 200,
"records": history_data,
"count": count
}
time.sleep(0.5)
return data


api.add_resource(SelectData, '/api/select_data')


代码说明:

(1)​sql1​是用来查询数据的,查出来后,返回给前端,渲染到列表中;​sql2​是用来查询数据总量的,显示当前查询条件下共有多少条数据;

(2)这里定义该接口为get请求,所以用request.args.get来获取前端传来的参数;


注意:在提取日期参数时,是这样提取的

​create_date = request.args.getlist("create_date[]")​​因为前端传来的​​create_date​​为数组,并且这个是get请求,所以flask在提取这种参数时需要使用​​getlist​​(3)处理分页时,在sql中使用​​LIMIT​​来实现返回对应数据,如下


假如每页显示10条,那么

第1页的数据为1~10,

第2页的数据为11~20,

第3页的数据为21~30,依此类推


对应到sql中limit方法下,

第1页数据为limit 0, 10; 从第1行开始,检索10条记录

第2页数据为limit 10, 10; 从第11行开始,检索10条记录,也就是11~20

第3页数据为limit 20, 10; 从第21行开始,检索10条记录,也就是21~30


了解这个对应关系后,我们从前端获取到 ​​当前页码 page_num​​ 和 ​​每页显示数据条数page_size​​后,就可以写出如下sql

flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_16


这里查出来的数据为元组,如果直接返回到前端会解析为列表


flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_17


前端请求后,接口返回如下


flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_18


5、前端发送请求,处理接口返回数据

在​​submitForm()​​方法中添加axios发送请求

submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let url1 = "http://127.0.0.1:5000/"
let payload = { // 定义请求参数
class_type: this.form.class,
create_date: this.form.create_date,
page_num: this.currentPage,
page_size: this.pageSize
}
console.log("打印查询条件输入的参数payload")
console.log(payload)
console.log("打印日期框选择框填写的日期")
console.log(this.form.create_date)
axios({
timeout: 10000,
method: "get",
params: payload, //发送get请求,使用params关键字接收请求参数
url: url1+"api/select_data"
}).then(res => {
console.log(res.data.records) //打印返回的原始数据
let data_list = res.data.records.map(function(array) {
let rObj = {};
rObj["date"] = array[2]
rObj["type"] = array[0]
rObj["value"] = array[1]
return rObj;})
console.log(data_list)
this.tableData = data_list
// let data_count = res.data.count
this.count = res.data.count

// console.log(data_list)
// console.log(data_count)

if(res.data.code === 200){ //判断响应中的code是否为200
// console.log(res.data)

this.$message({ // 接口调用成功后,添加页面toast提示
message: '接口调用成功',
type: 'success'
});
}
else{
console.log(res.data)
}
}).catch((reason)=>{
console.log(reason)
this.$message({
message: '接口调用失败,请检查系统是否正常',
type: 'warning'
});
})
// console.log(typeof this.currentPage)
} else {
console.log('error submit!!');
return false;
}
});
},


代码说明:

1、​​res.data.records​​ 是发送请求后返回的原始数据,即接口中定义的records

但是它的格式如下,不能直接给前端列表用

flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_19


前端列表需要如下格式的数据


flask+vue:创建一个数据列表并实现简单的查询功能(一)_创建日期_20


所以我们需要把里面一个个小的数组转换为对象

可以通过map来实现,代码如下


flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_21


在map中定义了一个函数,它的作用就是构造一个对象,分别用date、type、value作为键,然后分别赋上接口返回数组中每个小数组对应的值,这样处理后,接口返回的数组就变为了如下形式

flask+vue:创建一个数据列表并实现简单的查询功能(一)_sql_22

2、this.count = res.data.count

它的作用是把接口返回的数据总数赋给count

之前在分页组件中我们把count的值赋给了total,如下


flask+vue:创建一个数据列表并实现简单的查询功能(一)_创建日期_23


到这里为止,基本目的就达到了,从后端取出数据渲染到前端,同时可以分页、显示数据总量、并且可以查询


flask+vue:创建一个数据列表并实现简单的查询功能(一)_数据_24