之前寫過一個數據構造工具,當時用的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
用來接收接口返回並處理后的列表數據
其中currentPage
和pageSize
,分別表示當前頁碼和每頁條數,等會兒給請求傳參時,我們會用到它倆,所以我們用這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_num
、page_size
,這倆參數其實是我傳給后端請求接口中的2個參數
它們分別接收data()中的currentPage
和pageSize
的值
在控制台打印下結果,可以看到每次切換當前條數和頁碼,都能獲取到最新的值
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
這里查出來的數據為元組,如果直接返回到前端會解析為列表
前端請求后,接口返回如下
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,如下
到這里為止,基本目的就達到了,從后端取出數據渲染到前端,同時可以分頁、顯示數據總量、並且可以查詢