堅持原創,堅持實踐,堅持乾貨,如果你覺得有用,請點擊推薦,也歡迎關注我博客園和微信公眾號。
測試開發【提測平台】分享12-掌握日期組件&列表狀態格式化最終實現提測管理多條件搜索展示功能
微信搜索【大奇測試開】,關注這個堅持分享測試開發乾貨的傢伙。
本章內容思維導圖如下,由於需要各種狀態下的菜單操作,所以需要先實現提測信息的列表基礎頁面,然後再推進其他需求開發
基本知識點學習
DatePicker 日期組件
Element ui 組件中有兩個時間相關的控件,一個是 TimePicker 組件,以十分秒維度,另一個是 DatePicker 組件,以年月日時分秒維度,兩者用法基本相同,本篇開發用到的是DatePicker,它的基本使用方法為
<el-date-picker v-model="綁定變量值" align="right" type="date" placeholder="選擇日期"> </el-date-picker>
其中v-model一如即往的是指綁定的變量參數,align是對其的方向,placeholder標題沒有值的時候提示信息,最重要的type是指日期的樣式和類型,本例子選中時間後將展示如:2021-10-10 的格式時間,其他類型還報告年、月、星期、時間範圍月範圍等,實戰開發還會用到帶快捷選項 picker-options 屬性,即可以日期選項左側可以設定和快捷選項一個範圍,比如近一個月,近三個月等,樣式如,更多內容參考官方例子 [註解-1]
Select 選擇器
這個組件在之前的文章是講過的,當時直接舉例沒有講清楚具體使用場景,這裡舉例說明下:
場景1: 如果是少量比較固定的,可以直接前端寫<el-option key=””,value=””,label=””/>
場景2: 如果是動態固定數據,則用v-for的語法綁定一個list選項(用到之前請求接口賦予)
場景3: 如果是動態大量數據,可以使用遠程搜索模式,即數據關鍵詞後時時請求接口後台查詢後顯示,如公司用戶查詢選擇。
選項中key關鍵詞、value表示選擇後的值,lebel為實際展示的值
SQL聯合表查詢
依稀記得在上學的時候設計數據表的時候,尤其是關係型數據庫表,有很多的關聯表,也很規範的使用外鍵等,不過目前從工作後,尤其是近幾年很少看到這麼嚴謹了,基本上如果查詢少就在新表中多個相同字段,否則也是簡單關聯查詢,語法格式為 select A.*,B.字段 from A, B where A.id=B.aid
在項目編寫後端多條件查詢語句的時候,最好是設計相關表後,插入幾條關聯數據,在數據庫工具里先寫好關聯查詢語句,測試通過後在去拼寫後端請求代碼,千萬不要自信滿滿,筆記就是直接上代碼,可以由於空格,語法等就要浪費好多時間去一點點debug,就本文帶有時間範圍的聯合表查詢,可以先完全先寫sql語句,進行測試,然後再粘貼到代碼里實現,比如是我先實現的語句驗證OK。
需求功能實現
提測列表的搜索和展示需求其實和之前分享的應用列表管理非常類似,這裡就不再貼需求原型,直接給出實現部分和注意點,最後看下整體實現。
服務查詢接口
1. 查詢接口的數據字典
直接給出如下,方便大家直接調試
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for request -- ---------------------------- DROP TABLE IF EXISTS `request`; CREATE TABLE `request` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '自增ID', `title` varchar(200) DEFAULT NULL COMMENT '提測標題', `appId` varchar(50) DEFAULT NULL COMMENT '應用服務', `developer` varchar(255) DEFAULT NULL COMMENT '提測RD', `tester` varchar(255) DEFAULT NULL COMMENT '測試QA', `CcMail` varchar(500) DEFAULT NULL COMMENT '關係人', `verison` varchar(100) DEFAULT NULL COMMENT '提測版本', `type` tinyint(1) DEFAULT NULL COMMENT '提測類型 1.功能 2.性能 3.安全', `scope` text COMMENT '測試說明', `gitCode` varchar(200) DEFAULT NULL COMMENT '項目代碼', `wiki` varchar(200) DEFAULT NULL COMMENT '產品文檔', `more` text COMMENT '是否發送郵件,0未操作,1成功,2失敗', `status` tinyint(1) DEFAULT NULL COMMENT '測試狀態 1-已提測 2-測試中 3-通過 4-失敗 9-廢棄', `sendEmail` tinyint(1) DEFAULT NULL COMMENT '是否發送消息,0未操作,1成功,2失敗', `isDel` tinyint(1) DEFAULT '0' COMMENT '狀態0正常1刪除', `createUser` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '創建人', `createDate` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間', `updateUser` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '修改人', `updateDate` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; SET FOREIGN_KEY_CHECKS = 1;
2. 後端提測數據查詢接口
新建文件testmanager.py,沿用數據鏈接池不變,這裡主要注意代碼里的拼接,按照知識點裏說最好現在把測試語句在數據庫查詢命令行中測試通過,注意需要支持分頁。
# -*- coding:utf-8 -*- # testmanager.py from flask import Blueprint from dbutils.pooled_db import PooledDB from configs import config, format from flask import request import pymysql.cursors import json # 使用數據庫連接池的方式鏈接數據庫,提高資源利用率 pool = PooledDB(pymysql, mincached=2, maxcached=5,host=config.MYSQL_HOST, port=config.MYSQL_PORT, user=config.MYSQL_USER, passwd= config.MYSQL_PASSWORD, database=config.MYSQL_DATABASE, cursorclass=pymysql.cursors.DictCursor) test_manager = Blueprint("test_manager", __name__) @test_manager.route("/api/test/search",methods=['POST']) def searchBykey(): body = request.get_data() body = json.loads(body) # 基礎語句定義 sql = "" # 獲取pageSize和 pageSize = 10 if 'pageSize' not in body or body['pageSize'] is None else body['pageSize'] currentPage = 1 if 'currentPage' not in body or body['currentPage'] is None else body['currentPage'] # 拼接查詢條件 if 'productId' in body and body['productId'] != '': sql = sql + " AND A.productId LIKE '%{}%'".format(body['productId']) if 'appId' in body and body['appId'] != '': sql = sql + " AND A.appId LIKE '%{}%'".format(body['appId']) if 'tester' in body and body['tester'] != '': sql = sql + " AND R.tester LIKE '%{}%'".format(body['tester']) if 'developer' in body and body['developer'] != '': sql = sql + " AND R.developer LIKE '%{}%'".format(body['developer']) if 'status' in body and body['status'] != '': sql = sql + " AND R.status = '{}'".format(body['status']) if 'pickTime' in body and body['pickTime'] != '': sql = sql + " AND R.updateDate >= '{}' and R.updateDate <= '{}' ".format(body['pickTime'][0],body['pickTime'][1]) # 排序和頁數拼接 sql = sql + ' ORDER BY R.updateDate DESC LIMIT {},{}'.format((currentPage - 1) * pageSize, pageSize) print(sql) # 使用連接池鏈接數據庫 connection = pool.connection() with connection: # 先查詢總數 with connection.cursor() as cursor: count_select = 'SELECT COUNT(*) as `count` FROM request as R , apps as A where R.appId = A.id AND R.isDel=0' + sql print(count_select) cursor.execute(count_select) total = cursor.fetchall() # 執行查詢 with connection.cursor() as cursor: # 按照條件進行查詢 cursor.execute('SELECT A.appId,R.* FROM request as R , apps as A where R.appId = A.id AND R.isDel=0' + sql) data = cursor.fetchall() # 按分頁模版返回查詢數據 response = format.resp_format_success response['data'] = data response['total'] = total[0]['count'] return response
前端多條件搜索
此區域位於提測vue頁面的頂部,有兩個下拉選擇框,分別是「歸屬分類」 數據從服務接口獲,「測試狀態」 所有選項只有幾個前端寫了就行,點擊查詢按鈕進行接口請求和查詢,其中一個「新建提測」按鈕為預留,下一篇新頁面路由跳轉的邏輯。
1. HTML部分代碼參考
<div class="filter-container"> <el-form :inline="true" :model="search"> <el-form-item label="歸屬分類"> <el-select v-model="search.productId"> <el-option value="" label="所有" /> <el-option v-for="item in optsProduct" :key="item.id" :label="item.title" :value="item.id" /> </el-select> </el-form-item> <el-form-item label="應用ID"> <el-input v-model="search.appId" placeholder="服務ID關鍵詞" style="width: 200px;" clearable /> </el-form-item> <el-form-item label="測試人"> <el-input v-model="search.tester" placeholder="默認測試" style="width: 210px;" clearable /> </el-form-item> <el-form-item label="提測人"> <el-input v-model="search.developer" placeholder="默認測試" style="width: 210px;" clearable /> </el-form-item> <el-form-item label="時間範圍"> <el-date-picker v-model="search.pickTime" type="datetimerange" :picker-options="pickerOptions" range-separator="至" start-placeholder="開始日期" end-placeholder="結束日期" align="right"> </el-date-picker> </el-form-item> <el-form-item label="測試狀態"> <el-select v-model="search.status" placeholder="請選擇"> <el-option value="" label="所有" /> <el-option key=1 label="已提測" value=1></el-option> <el-option key=2 label="測試中" value=2></el-option> <el-option key=3 label="通過" value=3></el-option> <el-option key=4 label="失敗" value=4></el-option> <el-option key=9 label="廢棄" value=9></el-option> </el-select> </el-form-item> <el-form-item> <el-button type="primary" plain @click="searchClick()">查詢</el-button> </el-form-item> </el-form> <el-button type="primary" icon="el-icon-plus" style="float:right" @click="doCommit()">新建提測</el-button> </div>
2. JS部分所涉及代碼參考
所屬分類的接口請求不需要新寫,沿用應用管理那部分實現
1)data() 變量定義
主要是綁定的搜索條件、日期快捷,順便定義table存儲變量
import { apiAppsProduct } from '@/api/apps' // 新定義的提測api js文件 import { apiTestSearch } from '@/api/test.js' data() { return { // 條件查詢變量定義 search: { productId: '', appId: '', developer: '', tester: '', status: '', pickTime: '' }, // 範圍日期組件的快捷選項配置 pickerOptions: { shortcuts: [{ text: '最近一周', onClick(picker) { const end = new Date() const start = new Date() // 當前時間 start.setTime(start.getTime() - 3600 * 1000 * 24 * 7) //單位毫秒加減計算 picker.$emit('pick', [start, end]) } }, { text: '最近一個月', onClick(picker) { const end = new Date() const start = new Date() start.setTime(start.getTime() - 3600 * 1000 * 24 * 30) picker.$emit('pick', [start, end]) } }] }, // 所屬分類選項-沿用應用管理 optsProduct: [], // 數據表格展示和分頁變量數據定義 testData: [], pageValues: { pageSize: 10, currentPage: 1, total: 0 } } }
2. methods方法相關實現
默認歸類數據的請求與賦值,以及點擊搜索按鈕後的查詢接口請求和表格數據回填,頁碼數據回填。
// 所屬歸類選項數據查詢 productList() { apiAppsProduct().then(resp => { this.optsProduct = resp.data }) }, // 按照條件查詢,如果某個控件為空,則不會此字段請求 searchClick() { const body = { pageSize: this.pageValues.pageSize, currentPage: this.pageValues.currentPage, productId: this.search.productId, appId: this.search.appId, tester: this.search.tester, developer: this.search.developer, pickTime: this.search.pickTime, status: this.search.status } apiTestSearch(body).then(response => { // 將返回的結果賦值給表格自動匹配 this.testData = response.data this.pageValues.total = response.total }) },
數據展示和操作邏輯
上邊已經完成了所有數據綁定部分,接着就是進行展示
1. 分別定義 table 和 pagination 元素
這裡特別要關注實現就是操作的功能的按鈕要根據每條數據的狀態數值判斷是否給予顯示。之前需求文檔給出來的邏輯如下(測試中狀態有更正)
測試狀態 |
狀態碼 |
操作菜單 |
已提測 |
1(新建默認) |
開始測試 / 編輯提測 / 提測詳細 |
測試中 |
2 |
添加結果 / 編輯提測 / 提測詳細 |
通過 |
3 |
查看報告 / 編輯結果 / 提測詳細 |
失敗 |
4 |
查看報告 / 編輯結果 / 提測詳細 |
廢棄 |
9 |
刪除提測 / 編輯結果 / 提測詳細 |
html代碼參考
<div> <el-table :data="testData"> <!--:data prop綁定{}中的key,label為自定義顯示的列表頭--> <el-table-column prop="appId" label="應用ID" /> <el-table-column prop="title" label="提測標題" show-overflow-tooltip /> <el-table-column :formatter="formatStatus" prop="status" label="測試狀態"/> <el-table-column :formatter="formatType" prop="type" label="類型" /> <el-table-column prop="developer" label="提測人" /> <el-table-column prop="tester" label="測試人" /> <el-table-column prop="updateUser" label="更新人" /> <el-table-column :formatter="formatDate" prop="updateDate" label="更新時間" /> <el-table-column label="操作" width="300"> <template slot-scope="scope"> <!--<label>菜單邏輯判斷一列</label>--> <el-link type="primary" v-if="scope.row.status===1" @click="startTest(scope.row)">開始測試</el-link> <el-link type="primary" v-if="scope.row.status===2" >添加結果</el-link> <el-link type="primary" v-if="scope.row.status===3 || scope.row.status == 4" >查看報告</el-link> <el-link type="primary" v-if="scope.row.status===9" >刪除結果</el-link> <!--<label>菜單邏輯判斷二列</label>--> <el-divider direction="vertical"></el-divider> <el-link type="primary" v-if="[1,2].includes(scope.row.status)">編輯提測</el-link> <el-link type="primary" v-if="[3,4,9].includes(scope.row.status)">編輯結果</el-link> <el-divider direction="vertical"></el-divider> <el-link type="primary" >提測詳情</el-link> </template> </el-table-column> </el-table> </div> <div> <br> <el-pagination background :current-page.sync="pageValues.currentPage" :page-size="pageValues.pageSize" layout="total, sizes, prev, pager, next" :page-sizes="[5, 10, 20, 30, 50]" :total="pageValues.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div>
2. 對應元素JS代碼
分別要對時間格式化(之前有案例),狀態和類型列也要格式化,使用javascript 中的swith case 語法語句進行判斷,實現將本來的不可辨別的數字翻譯成對應的文字展示。
formatDate(row, column) { const date = row[column.property] if (date === undefined) { return '' } // 使用moment格式化時間,由於我的數據庫是默認時區,偏移量設置0,各自根據情況進行配置 return moment(date).utcOffset(0).format('YYYY-MM-DD HH:mm') }, formatStatus(row, column) { const status = row[column.property] switch (status) { case 1: return '已提測' case 2: return '測試中' case 3: return '通過' case 4: return '失敗' case 9: return '已廢棄' default: return '未知狀態' } }, formatType(row, column) { const type = row[column.property] switch (type) { case 1: return '功能測試' case 2: return '性能測試' case 3: return '安全測試' default: return '未知狀態' } },
另外一部分js代碼是對分頁的實現,請自行實現或參考git上源碼。
至此前後的邏輯代碼部分已經全部給出來了,沒有給出部分是api請求request 和本頁的菜單配置,相信如果你是一步步跟着之前文章實戰下來的,這些已經太輕車熟路了,啟動前後端最終實現頁面如下,最後再想想需要寫哪些CASE對本功能進行一個全面的測試。
學習交流群已經開放,可以關注公眾號【大奇測試開發】發送 「測試開發」 獲取最新二維碼入群。
【代碼更新】
-
地址://github.com/mrzcode/TestProjectManagement
-
TAG:TPMShare12
【註解&參考】
-
[註解-1] : //element.eleme.io/#/zh-CN/component/date-picker