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

之前写过一个数据构造工具,当时用的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>

实现效果

  

代码说明:

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"

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']"

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

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

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

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

 

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

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

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

create_date的值为''

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

create_date的值为['']

 

可以看到创建日期默认为空时,传的值为''

先赋值再重置,传的值为['']

所以后端处理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

 

3、添加分页功能

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

样式代码

<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用来接收接口返回并处理后的列表数据

 

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

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

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

 

(1)利用.sync 修饰符

html中,给当前页码参数current-page通过.sync 修饰符绑定currentPage

这样当前页码变化时,currentPage也会获取到最新的值

(2)利用size-change事件

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

 

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

 

handleSizeChange()中,我把回调参数val的值赋给pageSize参数,

这样pageSize就能得到最新的每页条数了

可以直接把val赋给pageSize:this.pageSize = val

同时,在组件中仍然要给page-size赋一个初始值,这样每次刷新页面,当前每页条数就显示这个定义的初始值

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

 

它们分别接收data()中的currentPagepageSize的值

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

 

4、后端处理

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

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

前端会传4个参数:class_typecreate_datepage_numpage_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

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

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

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

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

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

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

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

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

2、this.count = res.data.count

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

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

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

 

 

 

posted @ 2022-01-16 21:17  我是冰霜  阅读(2805)  评论(2编辑  收藏  举报