• 基于elementui input完成的输入控件


    背景

     

    基于业务需求,产品提出输入交互,element-ui本身不能支持。完整交互如下视频。

    IMG

    大致描述:输入框可输入多条数据,超过一条,则以数量的 +n的形式显示,用户可以在输入框中输入,也可以点击右边的编辑小手,在展开的textarea中输入数据。数据以回车,中文逗号,英文逗号分隔(后续传给服务端时需要组成数组的形式。)

    开发基本思路:

    由于我们项目系统使用的elementui框架,所以为了保持风格统一,使用el-input作为载体,上面的输入框使用默认 type='text',下方的输入框使用 type='textarea'。

    其中缩写的小方块用div元素包装,通过绝对定位 使其置于input输入框之上。

    下面是html结构

    1. <div class="input-wrap" @click.stop>
    2. <el-input
    3. size="mini"
    4. id="tag-input"
    5. v-model="inputVal"
    6. :class="customClass"
    7. class="y-w-160"
    8. :placeholder="placeholder">
    9. el-input>
    10. <i class="el-icon-edit" @click="showTextArea = !showTextArea">i>
    11. <div class="tagcut" v-if="contentArr.length > 0">
    12. <span class="y-c-p el-tag el-tag--info el-tag--mini el-tag--light f-tag" @click="showTextArea = true">
    13. <span class="el-select__tags-text">{{ contentArr[0] }}span>
    14. <i class="y-bk-C0C4CC el-tag__close el-icon-close" @click="delContent">i>
    15. span>
    16. <span class="y-c-p el-tag el-tag--info el-tag--mini el-tag--light" @click="showTextArea = true" v-if="contentArr.length > 1">
    17. <span class="el-select__tags-text">+ {{ contentArr.length - 1 }}span>
    18. span>
    19. div>
    20. <transition name="text-area">
    21. <div class="textareabox" v-if="showTextArea">
    22. <el-input
    23. placeholder="请输入单号、最多20个,请用,或者换行隔开"
    24. class="t-setting"
    25. :rows="8"
    26. size="mini"
    27. resize="none"
    28. v-model="localText"
    29. type="textarea">
    30. el-input>
    31. div>
    32. transition>
    33. div>

    有几个关键点这里提一下:

    1. 当输入数据时,输入框中会生成tag(div包裹的内容),覆盖在input框之上,这个时候需要处理一下input框的光标位置,即padding-left的值,以免被tag框遮挡。

    处理方式:监听textarea中的内容变化,当tag框有值时,获取tag框的宽度,动态设置input框的padding-left。代码如下

    1. watch: {
    2. localText(val) {
    3. // 处理输入的单号
    4. this.contentArr = val.replace(/\n/g, ',').replace(/,/g, ',').split(',').filter(item => item.trim());
    5. if(this.contentArr.length > this.maxLength) {
    6. this.$message.error('输入不能超过' + this.maxLength + '条');
    7. // this.contentArr中有重复数据
    8. let idx = 0; // 标记 this.contentArr[this.maxLength - 1] 这条数据的位置
    9. let maxLengthArr = this.contentArr.slice(0, this.maxLength);
    10. maxLengthArr.forEach((item, index) => {
    11. if(item === maxLengthArr[this.maxLength - 1]) {
    12. idx++;
    13. }
    14. })
    15. // 查找字符串中字符的位置做截取
    16. let indexOfNum = getIndexofNum(val, maxLengthArr[this.maxLength - 1], idx - 1);
    17. this.localText = val.slice(0, indexOfNum + maxLengthArr[this.maxLength - 1].length);
    18. }
    19. this.$nextTick(() => {
    20. let tagcutref = document.getElementsByClassName('tagcut');
    21. if(tagcutref[0]) {
    22. document.getElementById('tag-input').style.paddingLeft = tagcutref[0].offsetWidth + 2 + 'px';
    23. } else {
    24. document.getElementById('tag-input').style.paddingLeft = '10px';
    25. }
    26. })
    27. this.$emit('change', this.localText);
    28. }
    29. }

    2. 该输入框中的内容通过v-model双向绑定。关键代码

    1. export default {
    2. props: {
    3. textareaContent: {
    4. type: String,
    5. required: true
    6. },
    7. },
    8. model: {
    9. prop: 'textareaContent',
    10. event: 'change', // 通过$emti('change'),修改textareaContent的值
    11. },
    12. watch: {
    13. inputVal(val) {
    14. // 监听 , ,
    15. if(val.indexOf(',') !== -1 || val.indexOf(',') !== -1) {
    16. let inputValArr = val.replace(/,/g, ',').split(',').filter(item => item.trim());
    17. inputValArr.forEach(item => {
    18. if(this.localText) {
    19. // 如果有值
    20. this.localText = this.localText + '\n' + item;
    21. } else {
    22. // 第一条数据不需要换行
    23. this.localText = this.localText + item;
    24. }
    25. this.$emit('change', this.localText); // 双向绑定输入框中的内容
    26. })
    27. this.inputVal = ''; // 输入框数据处理之后 置空
    28. }
    29. },
    30. }
    31. }

    3. 在input框通过粘贴输入内容,复制的内容如果有换行符(\n),则会被转换成空格,不符合我们规定的数据输入格式(换行,中文逗号,英文逗号)。所以这里的处理方式是将input框的粘贴功能禁掉,然后监听粘贴的内容,对粘贴的内容进行处理,直接转换成对应tag框的形式展示在input框中。在textarea框中,则展示粘贴内容的原文本。

    1. <el-input
    2. size="mini"
    3. @blur="handleEnter"
    4. id="tag-input"
    5. v-model="inputVal"
    6. @paste.native.capture.prevent="handlePaste"
    7. :class="customClass"
    8. class="y-w-160"
    9. :placeholder="placeholder">
    10. el-input>

           处理粘贴内容的时候有个比较麻烦的问题:如果粘贴的内容超过输入限制(这里默认是20条),则需要截取,截取之后需要保证复制的文本的格式(可能有中文逗号,英文逗号,换行符)。截取方法:找到第20条数据的下标位置。注意数据可能会有重复。

    1. // contentArr是数据格式化之后的数组,maxLength是最大可输入的数据长度,默认20。
    2. if(this.contentArr.length > this.maxLength) {
    3. this.$message.error('输入不能超过' + this.maxLength + '条');
    4. // this.contentArr中可能有重复数据
    5. let idx = 0; // 标记 this.contentArr[this.maxLength - 1] 这条数据在this.contentArr数组中是第几条数据
    6. let maxLengthArr = this.contentArr.slice(0, this.maxLength);
    7. maxLengthArr.forEach((item, index) => {
    8. if(item === maxLengthArr[this.maxLength - 1]) {
    9. idx++; // 如果有重复,标记是第几条重复数据,便于查找下标。
    10. }
    11. })
    12. // 查找字符串中字符的位置做截取
    13. let indexOfNum = getIndexofNum(val, maxLengthArr[this.maxLength - 1], idx);
    14. this.localText = val.slice(0, indexOfNum + maxLengthArr[this.maxLength - 1].length);
    15. }
    16. function getIndexofNum(str, cha, num) {
    17. let strn = ' ' + str + ' ';
    18. let x = -1;
    19. let i = 0;
    20. while(i < num) {
    21. x = strn.indexOf(cha, x + 1);
    22. if((/\n|\,|\,|\s/).test(strn[x - 1]) && (/\n|\,|\,|\s/).test(strn[x + 1])) {
    23. // 判断字符前面和字符后面是否有 (空格|,|,|换行符),前后都有,表示该字符完全匹配
    24. i++;
    25. }
    26. }
    27. return x;
    28. }

    下面是完整代码:

    1. <template>
    2. <div class="input-wrap" @click.stop>
    3. <el-input
    4. size="mini"
    5. @blur="handleEnter"
    6. id="tag-input"
    7. @keyup.enter.native="handleEnter"
    8. v-model="inputVal"
    9. @paste.native.capture.prevent="handlePaste"
    10. :class="customClass"
    11. class="y-w-160"
    12. :placeholder="placeholder">
    13. el-input>
    14. <i class="el-icon-edit" @click="showTextArea = !showTextArea">i>
    15. <div class="tagcut" v-if="contentArr.length > 0">
    16. <span class="y-c-p el-tag el-tag--info el-tag--mini el-tag--light f-tag" @click="showTextArea = true">
    17. <span class="el-select__tags-text">{{ contentArr[0] }}span>
    18. <i class="y-bk-C0C4CC el-tag__close el-icon-close" @click="delContent">i>
    19. span>
    20. <span class="y-c-p el-tag el-tag--info el-tag--mini el-tag--light" @click="showTextArea = true" v-if="contentArr.length > 1">
    21. <span class="el-select__tags-text">+ {{ contentArr.length - 1 }}span>
    22. span>
    23. div>
    24. <transition name="text-area">
    25. <div class="textareabox" v-if="showTextArea">
    26. <el-input
    27. placeholder="请输入单号、最多20个,请用,或者换行隔开"
    28. class="t-setting"
    29. :rows="8"
    30. size="mini"
    31. resize="none"
    32. v-model="localText"
    33. type="textarea">
    34. el-input>
    35. div>
    36. transition>
    37. div>
    38. template>

     README.md

    1. # 运单号/订单号输入组件 -- 使用方法 -- 参数&事件说明
    2. ## @param v-model 双向绑定 输入内容
    3. * 说明:输入框中的内容
    4. * 是否必传: 是
    5. * 值类型:String
    6. ## @param placeholder
    7. * 说明:输入框的placeholder
    8. * 是否必传:否
    9. * 默认值:运单号
    10. * 值类型:String
    11. ## @params customClass
    12. * 说明:自定义的className
    13. * 是否必传:否
    14. * 默认值:'''
    15. * 值类型:String
    16. ## @example
    17. ```html
    18. customClass="y-w-300"
    19. v-model="waybillOrder"
    20. placeholder="订单号 " />

    功能不复杂, 主要在处理粘贴数据大于数据限制需要截取的时候, 当时思考的还算比较多。记录一下。

  • 相关阅读:
    机器学习——pca降维/交叉验证/网格交叉验证
    Springboot列车调度信息系统的设计与实现4guf9计算机毕业设计-课程设计-期末作业-毕设程序代做
    开源系统的组成组件(邮件,在线交流,版本控制)
    本地搭建svn服务器及TortoiseSVN的基本使用
    PHP社区果蔬网站毕业设计源码211548
    密码(6)
    【微服务】Nacos2.x服务发现?RPC调用?重试机制?
    Sqlmap 22.05.22.01
    2.15-CSS基础--vertical-align垂直对齐、光标类型、边框圆角
    SpringBoot之@ConfigurationProperties和@Value用法详解
  • 原文地址:https://blog.csdn.net/longgege001/article/details/127820289