基于Vue+Antd的Input输入框组件封装

2,068 阅读3分钟

需求分析

在开发过程中发现对ant-design的input输入框还缺失以下功能:

  1. 字数统计
  2. 字数限制
  3. 字节区分
  4. 回显只读模式
  5. 一键复制
  6. 长数据预览
  7. 预格式化展示
  8. 提示输入内容

经考虑分析后决定实现其中部分功能

保留

  1. 字数统计:统计当前输入的汉字、英文、数字的个数
  2. 字数限制:统计并限制当前输入的汉字、英文、数字的个数
  3. 回显只读模式:不能对内容进行编辑
  4. 一键复制:一键复制当前输入框中的内容
  5. 预格式化展示:按一定格式展示输入的内容

舍弃

  1. 字节区分:首先,在业务上,需要字节区分的就是少数;其次,字节编码方式众多,拿中文汉字举例,根据不同的编码方式中文字符占2~4个字节不等
  2. 长数据预览:长数据预览的问题存在于输入大量数据时输入框的长度有限,不能显示全部,修改数据需要多次移动光标,长数据预览可以解决显示问题但修改内容需多次移动光标的问题仍无法解决。数据预览功能可以使用ant-design提供的tooltip组件,故笔者认为没必要将冗余功能全部集成到输入框上
  3. 提示输入内容:提示输入内容大多用于提示用户输入格式,根据业务需求不同没有统一的实现规范,实现方式众多

效果展示

image.png image.png image.png image.png image.png

主要代码分析

插槽继承

<template v-for="(_, slot) of $options.omit(['suffix'],$scopedSlots)" #[slot]="scope" >
    <slot :name="slot" v-bind="scope" />
</template>
<template v-for="(_, slot) of $slots" :slot="slot">
    <slot :name="slot"></slot>
</template>
  1. 为什么要scopedSlotsscopedSlots、slots继承两次?虽然vue官方文档中指出slots会作为函数暴露在slots会作为函数暴露在scopedSlots中,但以这种形式(作用域插槽继承所有)继承,即使拿到具名插槽,也没办法处理其提供的作用域,故而还需要再继承一下具名插槽
  2. 下划线是占位符
  3. omit是Ramda中的函数,通过import引入,在export时也要放入,对自定义组件的使用要用$options

字数统计 字数限制

<template #suffix v-if="count">
  <span v-if="innerMaxLength" :class="{ changeNumber: innerMaxLength <= enter }">
    {{ enter + '/' + innerMaxLength}}
  </span>
  <span v-else :class="{ changeNumber: innerMaxLength <= enter }">
    {{ enter }}
  </span>
</template>

回显只读

<a-input
:value="value"
v-bind:readOnly="readOnly"
:class="{ readOnly: readOnly }"
...
>
...
</input>

//样式文件
.readOnly {
  border: @border-base;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  &:hover {
    border: @border-base;
  }
  &:focus {
    border: @border-base;
    box-shadow: none;
    border-color: #c0c4cc;
  }
}

ant-design中的输入框即使是只读状态,在聚焦时仍会有蓝色的边框和阴影,样式文件是为了去除其样式

一键复制

copyText() {
  //document进行选择是整个页面,如果同一个页面也用到这个组件,选中的就不一定是这个input框了,所以要用refs来绑定
  // var copyText = document.querySelector('#input');
  var copyText = this.$refs.input
  copyText.select()
  document.execCommand('copy')
  this.$message.success('复制成功!')
},

预格式化展示

watch: { //监听只能处理手动输入的情况
    value(newValue, oldValue) {
      if (this.$attrs.type === 'mobile') {
        // console.log('newValue.length', newValue.length);
        if (newValue.length > oldValue.length) {
          // 文本框中输入
          if (newValue.length === 3 || newValue.length === 8) {
            var insertTemp = this.value + this.delimiter;
            this.$emit('change', insertTemp);
          }
        } else {
          // 文本框中删除
          if (newValue.length === 9 || newValue.length === 4) {
            var delTemp = this.value.trim(); //如果不是空格应该就用不了trim
            this.$emit('change', delTemp);
          }
        }
      }
    },
},

封装结果&使用

使用案例:

<basic-input v-model="valueCount" allowClear count/>

<basic-input v-model="valueNumberLimit" count :maxLength="9" />

<basic-input v-model="valueMobile" type="mobile" placeholder="xxx xxxx xxxx" allowClear />

<basic-input v-model="valueReadOnly" readOnly />

<basic-input v-model="valueCopyAll" copyAll />

组件API

参数说明类型默认
count字数统计,配合字数限制会显示0/0的样式booleanfalse
readOnly是否只读状态booleanfalse
copyAll一键复制输入框内所有文字booleanfalse

源码地址

组件地址

项目地址

总结

有待完善 or 尚未解决

  1. 统计字数功能是占用了suffix功能,考虑不占用插槽的方式
  2. 一键复制功能占用了addonAfter功能,考虑不占用插槽的方式
  3. 预格式化展示功能只做了手机号一个功能,考虑其他格式
  4. 预格式化展示功能是使用watch,在粘贴复制时格式不正确
  5. 字节区分虽然舍弃了,但技术上要做到实现