+-
测试开发【提测平台】分享10-Element UI抽屉和表单校验&增改接口合并实现应用管理

微信搜索【大奇测试开】,关注这个坚持分享测试开发干货的家伙。

开篇说个小讨论,一个群里聊天聊到关于更新篇章的长度,是小篇幅多次,还是每次按照一个小完整的功能,我个人的是按照后种来的,主要的思考就是希望集中的时间段去实践,这样效率高且有每次会有一个小小的成就感,另一种当然有它的好处,更能持续输出,看的内容少,且很有助于阅读量。因此做个个小小的互动,欢迎留言,看看大家的想法~

本篇内容思维导

 服务应用管理,主要是前端的知识点,学习一种新的前端控件作为新增修改基层页,再重点掌握下表单数据提交的时候校验,这里不限于非空、格式等。对于后端的没有新增的知识点,可以参照项目产品分类管理的代码分别写新增和修改两个接口,也可按照这次我将两个接口合并成一个接口,其实就是之前有一章节留过的一个思考题“如何实现两个接口的合并”不知道还有没印象,总之本篇内容不是很多,加油~

 

知识点学习

Drawer 抽屉

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

<el-drawer
title="我是从右到左侧展示的抽屉"
: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

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

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

 

项目实战应用

按惯例,看完新知识点后,继续参照之前的产品原型和需求实现本期项目开发内容,即应用管理中的增加和修改功能原型和需求    

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

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

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

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

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

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

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

 

功能实现(步骤)伪代码

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

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

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

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

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

 

实践参考(本章)实现

1. 应用增改接口实现 

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

@app_application.route("/api/application/update",methods=['POST'])
def product_update():
# 获取传递的数据,并转换成JSON
body = request.get_data()
body = json.loads(body)

# 定义默认返回体
resp_success = format.resp_format_success
resp_failed = format.resp_format_failed

# 判断必填参数
if 'appId' not in body:
resp_failed.message = '应用不能为空'
return resp_failed
elif 'tester' not in body:
resp_failed.message = '测试负责人不能为空'
return resp_failed
elif 'developer' not in body:
resp_failed.message = '测试负责人不能为空'
return resp_failed
elif 'producer' not in body:
resp_failed.message = '产品负责人不能为空'
return

# 使用连接池链接数据库
connection = pool.connection()

# 判断增加或是修改逻辑
with connection:
# 如果传的值有ID,那么进行修改操作,否则为新增数据
if 'id' in body and body['id'] != '':
with connection.cursor() as cursor:
# 拼接修改语句,由于应用名不可修改,不需要做重复校验appId
sql = "UPDATE `apps` SET `productId`=%s, `note`=%s,`tester`=%s,`developer`=%s,`producer`=%s,`cCEmail`=%s, " \
"`gitCode`=%s, `wiki`=%s, `more`=%s, `creteUser`=%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["creteUser"], 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"] = 20001
resp_failed["message"] = "唯一编码keyCode已存在"
return resp_failed

with connection.cursor() as cursor:
# 拼接插入语句,并用参数化%s构造防止基本的SQL注入
# 其中id为自增,插入数据默认数据设置的当前时间
sql = "INSERT INTO `apps` (`appId`,`productId`,`note`,`tester`,`developer`,`producer`,`cCEmail`,`gitCode`" \
",`wiki`,`more`,`creteUser`,`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["creteUser"],body["updateUser"]))
# 提交执行保存新增数据
connection.commit()

return resp_success

2. 组合表单的抽屉控件实现

在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: '',
creteUser: '',
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-option
v-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>

 

3. 实现接口请求

在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.creteUser = this.op_user
this.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.id
this.appInfo.appId = row.appId
this.appInfo.productId = row.productId
this.appInfo.note = row.note
this.appInfo.tester = row.tester
this.appInfo.developer = row.developer
this.appInfo.producer = row.producer
this.appInfo.cCEmail = row.cCEmail
this.appInfo.gitCode = row.gitCode
this.appInfo.wiki = row.wiki
this.appInfo.more = row.more
this.appInfo.creteUser = ''
this.appInfo.updateUser = row.updateUser
},
commitApp() {      // 上边form定义ref,验证通过if valid的方式判断
this.$refs['appInfo'].validate((valid) => {
if (valid) {
this.appInfo.updateUser = this.op_user
apiAppsCommit(this.appInfo).then(response => {
// 如果request.js没有拦截即表示成功,给出对应提示和操作
this.$notify({
title: '成功',
message: this.appAction === 'ADD' ? '应用添加成功' : '应用修改成功',
type: 'success'
})
// 关闭对话框
this.drawerVisible = false
// 重新查询刷新数据显示
this.getProductList()
})
} else {
return false
}
})
}

 

4. 联调前后端运行

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

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

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

 

以上为本篇全部内容,目前应用管理的方面开发全部开发完了,后边将进入提测的主流程阶段。

 

设计开发中会遇到各种各样的问题,这些文章有我在写的时候都需要半天,有时候需要几天,因为总会有困难点和调试的问题,我相信大家在实践中更是如此,就即使你是完全复制粘贴的代码,但有问题我觉得是好事,这能让我们可以知其然,知其所以然,以及逐渐了解到解决问题方式。大家有什么问题可以留言交流或和关注公众号发私信,看到我会尽可能帮忙解答。

 

问题集锦

1. Form表单中的验证无效

本篇在开发整理中遇到了,form表单验证怎么也不生效的问题,搞了好久,最终是由于绑定的数据的方式弄混了,将 :mode 习惯的用了v-mode,另外也涉及了:ref定义一致性问题,如果你也遇到规则不生效请检查这些方面。

 

2.规则验证重置resetFields报错

在添加和修改的方法中,为了清除掉之前可能遗留的验证提示,使用了resetFields,但却忽略了它是需要依赖控件加载完成后才能调用,所以需要调在抽屉显示之后才调用,另外还需要使用到 this.$nextTick 回调延迟到下次DOM更新循环之后执行。

  

【代码更新】

地址:https://github.com/mrzcode/TestProjectManagement TAG:TPMShare10 

【注解&参考】

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

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

坚持原创,坚持实践,坚持干货,如果你觉得有用,请点击推荐,也欢迎关注我博客园和微信公众号。