【Flask】测试平台开发,应用管理模块实现-第十一篇

 概述

通过Element UI抽屉和表单校验&增改接口合并实现应用管理

Drawer 抽屉

之前产品修改和添加是使用Dialog组件实现的,但这个组件有时候并不满足我们的需求, 比如表单很长, 亦或是你需要临时展示一些文档, Drawer 是可以从侧面弹出的一个层,可以容纳更多的控件,优化交互体验。基本用法

<el-drawertitle="我是从右到左侧展示的抽屉":visible.sync="drawer"direction="rtl">这里可组合放其他组件Body部分
</el-drawer>
<!..script部分省略..>

显示和隐藏通过 visible 属性,类型是 boolean,当为 true 时显示 Drawer。Drawer 分为两个部分:title 和 body,title 可省略, direction为设置打开方向, Drawer 默认是从右往左打开,其他方向包括ltr(从左到右)、ttb(从上到下)、btt(从下往上),更多属性事件参考官方[注解1]

Form 表单验证

在之前的产品添加和修改功能都是直接提交的,一些验证是在后端做的处理,正常情况下,前端提交数据的时候就要进行一些如非空校验、是否为字符串、是否符合正则规则等,这里Form 组件是直接提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,支持默认属性绑定和自定义校验。更多参考[注解2],示例代码:

<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"><el-form-item label="绑定规则校验对应prop属性" prop="name"><el-input v-model="ruleForm.name"></el-input></el-form-item><el-form-item label="密码自定义校验" prop="pass"><el-input type="password" v-model="ruleForm.pass" autocomplete="off"></el-input></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>
<script>export default {data() {var validatePass = (rule, value, callback) => {if (value === '') {callback(new Error('请输入密码'));} else {if (this.ruleForm.checkPass !== '') {this.$refs.ruleForm.validateField('checkPass');}callback();}},return {ruleForm: {name: '',checkPass:''},rules: {name: [{ required: true, message: '请输入活动名称', trigger: 'blur' },{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }],pass: [{ validator: validatePass, trigger: 'blur' }]}};},methods: {submitForm(formName) {// 这里是提交前触发校验this.$refs[formName].validate((valid) => {if (valid) {alert('submit!');} else {console.log('error submit!!');return false;}});},resetForm(formName) {// 清除所有校验提示this.$refs[formName].resetFields();}}}
</script>

1)表单通过 :rules="rules" ref="ruleForm"进行数据和属性绑定

2)行项目中 prop=""去绑定规则对应的key,这里最好数据和验证的key保持一致

3)基本属性配置包括是否必须,字符的长短等,详细参考规则async-validator

  1. 自定义规则是一些较为复杂的校验通过回调进行逻辑自定义,参考例子第二个form-item

5)上述被定义required: true规则的控件在会自动增加 红色*号,trigger 定义什么时候触发校验

开发页面

此页面添加/修改功能需求说明:

  • 点击添加/编辑弹抽屉,红色为必填选项

  • 分类来源归属分类,外键关联

  • 应用名称有重名校验,创建后不可以修改

  • 默认必须有测试、研发和产品负责人,多人邮件用;分隔

  • 目前要求必须填写代码地址,以便测试人员了解信息,编写测试code

  • 以上数据字符长度暂无限制

功能实现(步骤)伪代码

  1. Python Flask 编写一个接口同时实现添加和修改数据功能

  2. 创建抽屉控件,内嵌form,实现原型中的各控件绑定

  3. 控件红色*标记的规则配置,触发方式trigger: 'blur'即点击提交统一校验,

  4. 页面修改和添加使用同一个Drawer 标题根据上一步操作动态显示 “应用添加” / “应用编辑”

  5. 应用编辑的自增ID不需要显示,应用ID不可编辑

实践参考实现

  1. 应用增改接口实现

这个合并接口的实现核心的逻辑点就是根据前端是否传了数据库id主键,如果有便认为是历史数据,走修改操作,否则走添加逻辑,完整代码和说明见代码:

@app_application.route("/api/application/update",methods=['POST'])
def product_update():# 获取传递的数据,并转换成JSONbody = request.get_data()body = json.loads(body)# 定义默认返回体resp_success = format.resp_format_successresp_failed = format.resp_format_failed# 判断必填参数if 'appId' not in body:resp_failed['message'] = '应用不能为空'return resp_failedelif 'tester' not in body:resp_failed['message'] = '测试负责人不能为空'return resp_failedelif 'developer' not in body:resp_failed['message'] = '测试负责人不能为空'return resp_failedelif 'producer' not in body:resp_failed['message'] = '产品负责人不能为空'returnif not body.get('note'):body['note'] = ''if not body.get('cCEmail'):body['cCEmail'] = ''if not body.get('gitCode'):body['gitCode'] = ''if not body.get('wiki'):body['wiki'] = ''if not body.get('more'):body['more'] = ''if not body.get('createUser'):body['createUser'] = ''if not body.get('updateUser'):body['updateUser'] = ''# 使用连接池链接数据库connection = pool.connection()# 判断增加或是修改逻辑with connection:# 如果传的值有ID,那么进行修改操作,否则为新增数据if 'id' in body and body['id'] != '':with connection.cursor() as cursor:# 拼接修改语句,由于应用名不可修改,不需要做重复校验appIdsql = "UPDATE `apps` SET `productId`=%s, `note`=%s,`tester`=%s,`developer`=%s,`producer`=%s,`cCEmail`=%s, " \"`gitCode`=%s, `wiki`=%s, `more`=%s, `updateUser`=%s, `updateDate`= NOW() WHERE id=%s"cursor.execute(sql, (body["productId"], body["note"], body["tester"], body["developer"], body['producer'], body["cCEmail"],body["gitCode"], body["wiki"], body["more"], body["updateUser"], body["id"]))# 提交执行保存更新数据connection.commit()else:# 新增需要判断appId是否重复with connection.cursor() as cursor:select = "SELECT * FROM `apps` WHERE `appId`=%s AND `status`=0"cursor.execute(select, (body["appId"],))result = cursor.fetchall()# 有数据说明存在相同值,封装提示直接返回if len(result) > 0:resp_failed["code"] = 20001resp_failed["message"] = "唯一编码keyCode已存在"return resp_failedwith connection.cursor() as cursor:# 拼接插入语句,并用参数化%s构造防止基本的SQL注入# 其中id为自增,插入数据默认数据设置的当前时间sql = "INSERT INTO `apps` (`appId`,`productId`,`note`,`tester`,`developer`,`producer`,`cCEmail`,`gitCode`" \",`wiki`,`more`,`createUser`,`updateUser`) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"cursor.execute(sql, (body["appId"],body["productId"], body["note"], body["tester"], body["developer"], body['producer'],body["cCEmail"],body["gitCode"],body["wiki"],body["more"],body["createUser"],body["updateUser"]))# 提交执行保存新增数据connection.commit()return resp_success
  • 组合表单的抽屉控件实现

在app.vue 代码文件<script></script> 添加绑定数据和基本规则

1)不要忘记登录人的变量导入

2)规则和请求变量的key一定确保一致

3)除了选择框未改变时候触发校验,其他都使用提交时候再做统一校验

data() {return {
// 获得登录的名字op_user: store.getters.name,// 定义动作appAction: 'ADD',// 控制抽屉显示隐藏drawerVisible: false,// 添加/修改绑定的数据appInfo: {id: '',appId: '',productId: '',note: '',tester: '',developer: '',producer: '',cCEmail: '',gitCode: '',wiki: '',more: '',createUser: '',updateUser: ''},// 规则设定rules: {appId: [{ required: true, message: '请输应用名称', trigger: 'blur' }],productId: [{ required: true, message: '请选择所属范围', trigger: 'change' }],tester: [{ required: true, message: '请输入测试负责人', trigger: 'blur' }],developer: [{ required: true, message: '请输入开发负责人', trigger: 'blur' }],producer: [{ required: true, message: '请输入产品负责人', trigger: 'blur' }]}}
}

在app.vue 代码文件 <div class="app-container"> </div>内编写组合控件

1)标题和appId是否可编辑根据 appAction 判断根据

2)归属分类沿用搜索里的下拉实现,也可以使用基本方式

3)实现规则一定注意el-form-item 中 prop 的定义和一致性

<el-drawer:title="appAction==='ADD'? '添加应用': '修改应用'":visible.sync="drawerVisible"size="45%"direction="rtl"><div><el-form :model="appInfo" :rules="rules" ref="appInfo" label-width="120px"><el-form-item label="应用ID" prop="appId" ><el-input v-model="appInfo.appId" :disabled="appAction==='ADD'? false : true" style="width: 300px"/></el-form-item><el-form-item label="归属分类" prop="productId"><el-select v-model="appInfo.productId" style="width: 300px"><el-optionv-for="item in options":key="item.id":label="item.title":value="item.id"><span style="float: left">{{ item.keyCode }}</span><span style="float: right; color: #8492a6; font-size: 13px">{{ item.title }}</span></el-option></el-select></el-form-item><el-form-item label="应用描述"><el-input v-model="appInfo.note" style="width: 300px"/></el-form-item><el-form-item label="测试负责" prop="tester"><el-input v-model="appInfo.tester" style="width: 300px"/></el-form-item><el-form-item label="研发负责" prop="developer"><el-input v-model="appInfo.developer" style="width: 300px"/></el-form-item><el-form-item label="产品负责" prop="producer"><el-input v-model="appInfo.producer" style="width: 300px"/></el-form-item><el-form-item label="默认抄送"><el-input v-model="appInfo.cCEmail" style="width: 300px"/></el-form-item><el-form-item label="代码地址"><el-input v-model="appInfo.gitCode" style="width: 300px"/></el-form-item><el-form-item label="相关wiki"><el-input v-model="appInfo.wiki" style="width: 300px"/></el-form-item><el-form-item label="更多信息"><el-input v-model="appInfo.more" style="width: 300px"/></el-form-item><el-form-item><span class="dialog-footer"><el-button @click="drawerVisible=false">取 消</el-button><el-button type="primary" @click="commitApp('appInfo')">提 交</el-button></span></el-form-item></el-form></div>
</el-drawer>
  • 实现接口请求

在app.js 定义/api/application/update接口模版请求

// 调用应用增加/修改统一接口
export function apiAppsCommit(requestBody) {return request({url: '/api/application/update',method: 'post',data: requestBody})
}

在app.vue 代码文件<script></script> 实现添加和修改请求方法

1)addApp上节的占位的方法体,这里要实现信息清空和动作定义

2)updateApp 同样,实现选择的数据反填和遗留信息清空基本操作

3)请求后端接口要在所有规则校验通过后才进行真正的提交

addApp() {// 定义动作,以抽屉做判断this.appAction = 'ADD'// 添加数据初始化this.appInfo.id = ''this.appInfo.appId = ''this.appInfo.productId = ''this.appInfo.note = ''this.appInfo.tester = ''this.appInfo.developer = ''this.appInfo.producer = ''this.appInfo.cCEmail = ''this.appInfo.gitCode = ''this.appInfo.wiki = ''this.appInfo.more = ''this.appInfo.createUser = this.op_userthis.appInfo.updateUser = this.op_user// 初始化完成后显示抽屉this.drawerVisible = true// 如果有遗留验证清空this.$nextTick(() => {this.$refs['appInfo'].resetFields()})},updateApp(row) {// 定义动作,以抽屉做判断this.appAction = 'UPDATE'// 初始化完成后显示抽屉this.drawerVisible = true// 如果有遗留验证清空this.$nextTick(() => {this.$refs['appInfo'].resetFields()})// 选择数据反填抽屉表单中this.appInfo.id = row.idthis.appInfo.appId = row.appIdthis.appInfo.productId = row.productIdthis.appInfo.note = row.notethis.appInfo.tester = row.testerthis.appInfo.developer = row.developerthis.appInfo.producer = row.producerthis.appInfo.cCEmail = row.cCEmailthis.appInfo.gitCode = row.gitCodethis.appInfo.wiki = row.wikithis.appInfo.more = row.morethis.appInfo.createUser = ''this.appInfo.updateUser = row.updateUser},commitApp() {// 上边form定义ref,验证通过if valid的方式判断this.$refs['appInfo'].validate((valid) => {if (valid) {this.appInfo.updateUser = this.op_userapiAppsCommit(this.appInfo).then(response => {// 如果request.js没有拦截即表示成功,给出对应提示和操作this.$notify({title: '成功',message: this.appAction === 'ADD' ? '应用添加成功' : '应用修改成功',type: 'success'})// 关闭对话框this.drawerVisible = false// 重新查询刷新数据显示this.getProductList()})} else {return false}})}
  • 联调前后端运行

分别运行前后端,解决掉运行中的错误后,做两条测试验证功能是否OK

1)添加操作,默认为空数据,提交不完整信息是否有校验提示阻止提交

2)编辑操作,数据是否正常反填,修改后提交是否正常更新落库

至此添加服务应用管理产品管理的功能模块我们就开发完成了,下一阶段我们将继续开发新的功能菜单栏--测试管理模块

【注解&参考】

  • [注解1] https://element.eleme.io/#/zh-CN/component/drawer

  • [注解2] https://element.eleme.io/#/zh-CN/component/form#biao-dan-yan-zheng

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/95608.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/95608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch 深分页限制与解决方案

最近在准备面试&#xff0c;正把平时积累的笔记、项目中遇到的问题与解决方案、对核心原理的理解&#xff0c;以及高频业务场景的应对策略系统梳理一遍&#xff0c;既能加深记忆&#xff0c;也能让知识体系更扎实&#xff0c;供大家参考&#xff0c;欢迎讨论。在项目中遇到一个…

基于偏最小二乘法PLS多输入单输出的回归预测【MATLAB】

基于偏最小二乘法&#xff08;PLS&#xff09;多输入单输出的回归预测【MATLAB】 在科学研究和工程实践中&#xff0c;我们常常需要根据多个相关变量来预测一个关键结果。例如&#xff0c;根据气温、湿度、风速等多个气象因素预测空气质量指数&#xff0c;或根据多种原材料成分…

SQL Server核心架构深度解析

SQL Server 的体系结构是一个复杂但设计精密的系统&#xff0c;主要可以分为四大核心组件&#xff0c;它们协同工作以管理数据库、处理查询、确保数据安全与一致性。以下是其体系结构的核心组成部分&#xff1a; 核心组件&#xff1a;协议层 (Protocol Layer) 作用&#xff1a;…

Django REST Framework Serializer 进阶教程

1. 序列化器概述 在 Django REST Framework&#xff08;DRF&#xff09;中&#xff0c;序列化器&#xff08;Serializer&#xff09;用于将复杂的数据类型&#xff08;如模型实例&#xff09;转换为 JSON 格式&#xff0c;以便于 API 返回给客户端。此外&#xff0c;序列化器还…

面试问题详解十四:Qt 多线程同步【QSemaphore】讲解

在多线程开发中&#xff0c;经常需要控制多个线程对共享资源的访问数量。例如限制同时下载文件的数量、控制数据库连接池的连接使用等等。这时候&#xff0c;Qt 提供的 QSemaphore&#xff08;信号量&#xff09;就非常派得上用场。一、什么是 QSemaphore&#xff1f; QSemapho…

Spark mapGroups 函数详解与多种用法示例

mapGroups 是 Spark 中一个强大的分组操作函数&#xff0c;它允许你对每个分组应用自定义逻辑并返回一个结果。以下是多个使用简单样例数据的具体用法示例。基础示例数据假设我们有一个简单的学生成绩数据集&#xff1a;// 创建示例DataFrame val studentScores Seq(("Ma…

【图论】Graphs.jl 图数据的读写与生成器

文章目录图数据的读写Graphs.loadgraphGraphs.loadgraphsGraphs.savegraph保存单个图保存图字典Graphs.loadlg_multGraphs.savelgGraphs.savelg_mult图的生成器1. 随机图模型1.1 Erdős–Rnyi 模型1.2 巴拉巴西-阿尔伯特模型 (无标度网络)1.3 小世界网络模型1.4 随机块模型 (SB…

Go指针全解析:从基础到实战

基本概念与定义指针的定义指针是一种特殊的变量类型&#xff0c;它存储的不是实际数据值&#xff0c;而是另一个变量在计算机内存中的地址。在底层实现上&#xff0c;指针本质上是保存内存位置的无符号整数&#xff0c;它直接指向内存中的特定位置&#xff0c;允许程序直接操作…

Oracle 查询有哪些用户 提示用户名密码无效

要查询 Oracle 数据库中的所有用户&#xff0c;可以使用以下 SQL 查询语句。这个查询将返回数据库中所有用户的列表。 [] SELECT username FROM all_users ORDER BY username;如果你有足够的权限&#xff08;通常是 DBA 权限&#xff09;&#xff0c;你也可以使用 dba_users 视…

小白成长之路-develops -jenkins部署lnmp平台

文章目录一、准备工作1.1两台虚拟机1.2配置文件1.3免密登录二、实战1.构建主item2.测试nginx,php,mysql2.1新建测试项目2.2与正式项目绑定构建后的操作2.3测试2.4导入discuz项目总结一、准备工作 1.1两台虚拟机 服务器&#xff1a;192.168.144.24 客户端&#xff1a;192.168.…

【HarmonyOS 6】仿AI唤起屏幕边缘流光特效

【HarmonyOS 6】仿AI唤起屏幕边缘流光特效 一、前言 最近在做 HarmonyOS 6.0 的适配&#xff0c;发现 Beta1版本里多了个很实用的视效功能——自带背景的双边流光。 之前做屏幕边缘流光特效的时候&#xff0c;要么得自己写渐变动画拼效果&#xff0c;要么就得套好几个组件叠层&…

跟做springboot尚品甄选项目

springbootvue3 【尚硅谷Java项目《尚品甄选》 SpringBootSpringCloud萌新学会企业级java项目】003.后台系统-搭建前端环境&#xff08;工程创建&#xff09;_哔哩哔哩_bilibili E:\project\AllProJect\Shangpin Selection\项目材料素材\课件\尚品甄选项目课件 前端套用框架…

【Linux】创建线程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 文章目录 一、为什么需要线程&#xff1f; 创建线程 示例&#xff1a;计算斐波恩夕法 一、为什么需要线程&#xff1f; 在多核处理器的计算机上&#xff0c;线程可…

HTML应用指南:利用POST请求获取全国九号电动车体验店服务店位置信息

九号公司(Ninebot)作为全球领先的智能短途出行解决方案提供商,始终秉持“智慧移动,愉悦生活”的品牌理念,致力于为个人用户打造安全、智能、时尚的城市出行体验。依托“智能硬件 + 数字服务 + 线下触点”三位一体的战略布局,九号公司已建立起覆盖全国、辐射全球的销售与服…

Kafka面试精讲 Day 4:Consumer消费者模型与消费组

【Kafka面试精讲 Day 4】Consumer消费者模型与消费组 在“Kafka面试精讲”系列的第四天&#xff0c;我们将深入探讨Kafka的核心组件之一——Consumer消费者模型与消费组&#xff08;Consumer Group&#xff09;。这是Kafka实现高吞吐、可扩展消息消费的关键机制&#xff0c;也…

使用 Uni-app 打包 外链地址APK 及 iOS 注意事项

本文详细介绍了如何使用 Uni-app 框架将项目打包为 Android APK 和 iOS 应用&#xff0c;重点讲解了 minSdkVersion、targetSdkVersion 和 abiFilters 的配置&#xff0c;以及 iOS 开发的注意事项。文章还包含了您提供的 WebView 示例代码&#xff0c;并提供了关键的注意事项&a…

异常处理小妙招——3.构造函数的安全第一原则:为什么不在构造函数中抛出异常?

文章目录灾难性的生日派对构造函数&#xff1a;对象的出生证明安全第一&#xff1a;严格的出生检查为什么要在构造函数中严格验证&#xff1f;1. 避免"僵尸对象"2. Fail-Fast&#xff08;快速失败&#xff09;原则现实世界的实践建议1. 使用工厂方法模式2. 使用Build…

iptables 和 ip route

文章目录iptables原理及常用命令表链链表链表总结iptables 常用命令及参数1. 规则管理命令 (Commands)2. 规则匹配参数 (Rule-Specification - Matches)3. 目标动作参数 (Target)命令示例配置流程示例ip route常用命令iptables和ip route的联系实用命令示例对比iptables原理及常…

RPC和HTTP的区别?

RPC和HTTP是两种不同的通信协议&#xff0c;它们在通信方式、性能效率以及灵活性可扩展性等方面存在区别。以下是具体分析&#xff1a; 通信方式 RPC&#xff1a;RPC是基于远程过程调用的二进制协议&#xff0c;它允许客户端像调用本地函数一样调用远程服务器上的函数或方法[2]…

贝叶斯分类(Bayes Classify)

一. 核心思想贝叶斯分类是一类基于贝叶斯定理&#xff08;Bayes Theorem&#xff09;和概率统计的分类算法&#xff0c;核心思想是 “通过已知的先验概率&#xff0c;结合数据的似然性&#xff0c;计算后验概率&#xff0c;最终将样本归为后验概率最高的类别”。它在机器学习、…