• 对element表单交互提示的二次修改
  • 发布于 2个月前
  • 173 热度
    0 评论
效果

优点
1.避免了 el-form-item 规则提示信息过长换行显示不全问题
2.聚焦直接显示规则状态变化,得以及时提醒用户输入
3.输入过程中,直观感受到所有规则以及校验情况
4.失焦隐藏提示,样式仍存在提示用户
5.直接触发表单校验,只有样式变化,整体画面更简洁

需求
1.聚焦组件显示提示,失焦隐藏(非必填情况聚焦不显示,不满足规则再显示)
2.首次聚焦,基于当前组件值展示校验结果
3.值变化,每个规则都要对只进行校验
4.存在任意一个规则错误,红框展示,全部正确,蓝框展示

解析思路
1.先说个题外话,在满足需求下,封装组件还有几个技术要求
  .原有页面替换使用方便
  .使用习惯尽量保持不变
  .原有功能不缺失
2.UI 大大这需求,明显是针对 el-form-item 组件;先看看如何满足技术要求
  .封装一个组件承接规则校验和提示处理(不满足)
  .在 el-form-item 基础上进行改造,并命名为新组件,替换使用(满足)
3.解析需求
  .采用 el-popover 进行提示,非必填情况通过 disabled 处理
  .首次聚焦主动触发规则校验函数
  .值变化,触发规则校验,直接复用 el-form-item 逻辑
  .状态变化直接复用 el-form-item 逻辑

实现
el-form-item 改造
.采用继承重写部分功能,达到低成本实现需求
  .通过设置 extends:FormItem、name:form-item-tip,让 form-item-tip 拥有 FormItem 能力
  .script 中选项 API,与 FormItem 一致的名称会被覆盖,不一致的会被合并处理
  .template 只能被覆盖,无法被合并处理
<template>
  <div class="el-form-item">
    .....
  </div>
</template>
<script>
import { FormItem } from 'element-ui'
export default {
    extends: FormItem,
    name: 'form-item-tip'
}
</script>
收集提示信息
.由于聚焦显示所有规则提示,需要收集到 rule 中所有 message,但是自定义规则的 message 是通过 callback 返回的,不执行的情况下无法得知,这里扩展了自定义规则的 message 字段
.为保证能动态变化,自定义规则的 message 字段可设置数组、函数类型
rules:{
    FucName: [
      {
        required: true,
        message: '名称不能为空',
        trigger: ['change'],
      },
      {
        validator: this.validLen,
        trigger: ['change'],
        message: ['支持长度为6-10个字符'],
      },
      {
        validator: this.validAll,
        trigger: ['change'],
        message: () => (['支持中文、字母、数字']),
      }
   ]
}
收集message
getErrorInfo() {
  // tip 状态
  const mesErrorObj = {}
  this.tipList.forEach((item) => {
    mesErrorObj[item.message] = item.isError
  })
  // 获取最新规则数组
  const curRules = this.getRules()
  this.tipList = curRules.reduce((acc, cur) => {
    const message = cur.message
    if (Array.isArray(message)) {
      return this.handlerArray(acc, message, mesErrorObj)
    }
    if (typeof message === 'function') {
      return this.handlerArray(acc, message(), mesErrorObj)
    }
    return [
      ...acc,
      {
        // 优先使用上一次状态
        isError: mesErrorObj[message] === undefined ? true : mesErrorObj[message],
        message,
      },
    ]
  }, [])
},
注:一个自定义规则 a 若返回三种提示信息,这种需要拆分为三个自定义规则,因为 a 返回规则1不符合时,规则2、3 是否符合无从得知(使用时需要额外注意)

提示模板
支持插槽修改提示内容,内置简单展示方式
<template>
  <div class="el-form-item">
    .....
    <div class="el-form-item__content" :style="contentStyle">
      <el-popover trigger="focus">
        <!-- 支持插槽 -->
        <slot name="tip" :tipList="tipList">
          <div class="tip-wrap">
            <div class="tip-item" v-for="(item, index) in tipList" :key="index">
              <i :class="[item.isError ? 'el-icon-error icon-error' : 'el-icon-success icon-success']"></i>
              <span>{{ item.message }}</span>
            </div>
          </div>
        </slot>
        <slot slot="reference"></slot>
      </el-popover>
    </div>
  </div>
</template>
聚焦触发校验
.聚焦组件会触发 el-popover 的 show 事件,在事件内进行处理即可
  .收集提示信息
  .触发 el-form-item 内部校验方法
  .更新 Popover 位置,避免出现错位
<el-popover @show="showPopover" ref="popoverRef"></el-popover>

updatePopover() {
  this.$nextTick(() => {
    this.$refs.popoverRef.updatePopper()
  })
},
showPopover() {
  this.getErrorInfo()
  this.validate('', noop)
  this.updatePopover()
},
非必填情况
采用 disabled 达到 el-popover 禁用不显示,情况如下:
  .未设置规则
  .设置规则 && 规则为非必填 && 组件 v-model 为空
<el-popover :disabled="popoverDisable" ref="popoverRef"></el-popover>

computed: {
    popoverDisable() {
      return !this.tipList.length || (this.tipList.length && !this.isRequired && this.fieldValueIsEmpty)
    },
    fieldValueIsEmpty() {
      return (
        this.fieldValue === '' || this.fieldValue === null || this.fieldValue === undefined || (Array.isArray(this.fieldValue) && this.fieldValue.length === 0)
      )
    },
},
watch: {
    popoverDisable(nVal, oVal) {
      if (!nVal && oVal) {
        // 从禁用到展示 -- 需要更新位置
        this.updatePopover()
      } else {
        // 从展示到禁用 -- 规则都符合
        this.validateState = 'success'
      }
    },
},
校验以及结果展示
1.el-form-item 校验函数名为 validate,需要在其内部加一些代码:
  . el-popover 禁用情况下,不再进行校验直接通过
  . 去除自定义规则情况下的自定义字段 message(否则 async-validator 无法运行得出正确结果)
  . 校验结果需要通过 tipAllMessage 函数对提示信息进行处理

2.基于 async-validator 处理结果更新提示信息状态 --- tipAllMessage 函数

  async-validataor 结果(特殊处理情况:必填情况下,值为空

 .包含

必填规则 message
自定义规则执行过程中 callback 中有的 message,
.不包含
不符合的其他配置规则 message
// 重写
validate(trigger, callback = noop) {
  this.validateDisabled = false
  const rules = this.getFilteredRule(trigger)
  .....
  if (this.popoverDisable) {
    callback('', null)
    return
  }
  ...
  if (rules && rules.length > 0) {
    rules.forEach((rule) => {
      delete rule.trigger
      if (rule.validator) {
        delete rule.message
      }
    })
  }
  ....
  validator.validate(model, { firstFields: false }, (errors, invalidFields) => {
    ....
    this.tipAllMessage(errors)
  })
},
// 更新提示信息状态
tipAllMessage(errors) {
  let errorMes = Array.isArray(errors) ? errors.map((item) => item.message) : []
  if (this.isRequired && this.fieldValueIsEmpty) {
  // 必填情况下,值为空,特殊处理
    errorMes = this.tipList
      .filter((item) => {
        // 其他配置规则 --- 留下
        if (!item.isValidator) {
          return true
        }
        // async-validataor 结果中有的 --- 留下
        if (errorMes.includes(item.message)) {
          return true
        }
        return false
      })
      .map((item) => item.message)
  }
  // errorMes 中存在的没通过,否则通过 --- 更新 tipList 中 isError 字段
},

最后
如果对你开发某些功能有所帮助,麻烦多点赞评论收藏😊
如果对你实现某类业务有所启发,麻烦多点赞评论收藏😊
大家觉得这种交互方式好嘛,欢迎留言交流哦!

用户评论