ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-09-21 09:03:38  阅读:121  来源: 互联网

标签:body 10 appInfo 增改 resp 校验 Element message row


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

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

本篇内容思维导

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

 

知识点学习

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

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

 

功能实现(步骤)伪代码

  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():
    # 获取传递的数据,并转换成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

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

标签:body,10,appInfo,增改,resp,校验,Element,message,row
来源: https://www.cnblogs.com/mrzcode/p/15315921.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有