【vxe-table】@enter.keyup.native实现在列表中回车光标向右移动聚焦及vxe-table的一些方法的使用(具体实现+踩坑篇)

需求:
vxe-table表格
1、新增的时候,vxe-table第一行的第一个输入框聚焦
2、输入完成后,按回车,自动跳到同一行的下一个输入框
3、当在同一行的最后一个输入框输入完成后,按回车跳回第一个输入框并选中状态且复选框为选中状态
4、点新增滚动条过长遮挡住第一列的话,要向左滚动到最开始的位置
5、表格中有两种下拉框,原生的lel-select及封装后的select组件,面板打开时的上下移动及回车赋值和直接点击赋值后要聚焦
6、表格中有封装的组件,输入框+el-table,输入值的时候显示el-table,使用的el-popover,实现类似于el-selelct下拉框面板的效果,增加了svg图标,点击打开弹窗,弹窗双击行或单击后点确定赋值之后,输入框要聚焦

需求+优化, 花了两天的时间,

踩过的坑:
1、el-select选择框面板打开,点击选值后,回车向右跳转后,el-seelct依然打开面板问题
2、列表同一行,任意位置开始按回车键,触发不了回车事件
3、回车事件中,获取不到自定义属性问题
4、列表同一行最后一列回车时跳到第一列中聚焦 且选中状态
5、列表横向过长,出现滚动条时新增,滚动条如何回到第一列且聚焦
6、回车向右跳到下一输入框聚焦时,滚动条遮挡导致下一输入框显示不全,滚动条如何滑至最右侧
7、vxe-table复选框如何手动选中且保持原有的勾选效果

实现思路;

1、在utils文件夹下定义一个js文件,全局注册后,给输入框,下拉框等编辑组件绑定回车事件
2、给所有列表Dom元素添加自定义索引

备注:示例代码只保留了和功能相关的代码

1、父页面中,vxe-table封装成子组件

    <vxeTable
              ref="vxeTableRef"           
              :columns="columns"
              :data-source="list"
              @selectAllEvent="selectAllEvent"
              @getList="getList"           
              @addRow="addRow"
              @handleDelList="handleDelList"
              :formData="formData"
              :enterFlag.sync="enterFlag"
            ></vxeTable>

   
//新增
handleAddList(){
    
    
this.list.unshift({
    
    
          id: this.uuid(),
          money: 0,
          cash: 0,
          checked:false
        })
        this.list.forEach(item=>{
    
    
          item.checked = false
        })
        this.dealFirstInput()
},

 dealFirstInput(index, flag) {
    
    
      this.$nextTick(() => {
    
    
        // vxe-body--row从1开始
              let lastIndex
        if (flag == 'up') {
    
    
          lastIndex = index +1
        } else if (flag == 'down') {
    
    
          lastIndex = index + 2
        }
        // 增加下行
        if (index>=0&&flag) {
    
    
          // 获取第一个输入框元素
          const lastElement = `.vxe-table--body-wrapper .vxe-table--body .vxe-body--row:nth-child(${
     
     lastIndex}) .vxe-body--column:nth-child(3) .el-input__inner`
          const tolastInput = this.$refs.vxeTableRef.$refs.vxeTable.$el.querySelector(lastElement)
          // // 聚焦第一个输入框
          tolastInput.focus()
        } else {
    
    
          // 新增和增加上行
          // 获取第一个输入框元素
          const firstInput = this.$refs.vxeTableRef.$refs.vxeTable.$el.querySelector(
            '.vxe-table--body-wrapper .vxe-table--body .vxe-body--row:nth-child(1) .vxe-body--column:nth-child(3) .el-input__inner'
          )
          // // 聚焦第一个输入框
          firstInput.focus()
        }

        this.enterFlag = true
      })
    },
//列表操作列增加上行,增加下行
    addRow(row, index, key) {
    
    
     const newObj = {
    
     money: 0, cash: 0, id: this.uuid() }
     if (index >= 0) {
    
    
          this.enterFlag = !this.enterFlag

          if (key === 'previous') {
    
    
            this.list.splice(index, 0, newObj)
            this.dealFirstInput(index, 'up')

            return
          }
          this.list.splice(index + 1, 0, newObj)
          this.dealFirstInput(index, 'down')
        }
        }

2、子组件vxe-table内部

    <el-form :model="form" ref="form" :rules="rules" :show-message="false">

      <vxe-table     
        ref="vxeTable"
        class="mytable-scrollbar"
        :row-config="{ height: 36 }"
        border
        @scroll="handleScroll"
        show-overflow
        resizable
        @checkbox-all="selectAllEvent"
        @checkbox-change="selectAllEvent"
        :footer-method="footerMethod"
        :data="list"
        :column-config="{ resizable: true }"
        @resizable-change="resizableChange"
      :checkbox-config="{ checkAll: true }"
     
      >
       <vxe-column type="checkbox" width="60" fixed="left" :header-align="'center'" :align="'center'" :resizable="false"></vxe-column>
         <template v-for="config in newColumns">
          <vxe-column
            :key="config.field"
            :width="config.width"
            :field="config.field"
            :visible="config.visible"
            :header-align="'center'" 
            :align="'center'"
            :fixed="config.freeze === true ? 'left' : ''"
            :min-width="config.field"
            :resizable="true"
          >
                    <template #default="{ row, $rowIndex, $columnIndex }" v-if="config.field == 'xxx'">
              <el-form-item :prop="'list.' + $rowIndex + '.notes'" :rules="rules.xxx">
                <el-input
                  ref="input"
                  v-model="row.xxx"
                  @keyup.enter.native="$event => tableKeydown($event, $rowIndex, $columnIndex)"              
                  clearable
                ></el-input>
              </el-form-item>
            </template>
                        <template #default="{ row, $rowIndex, $columnIndex }" v-else-if="config.field == 'xxx'">
              <el-select
                :ref="'select' + $columnIndex + 'input' + $rowIndex"
                v-model="row.xxx"
                @keyup.enter.native="$event => tableKeydown($event, $rowIndex, $columnIndex)"
                clearable
                filterable
                :disabled="isEnabled"
              >
                <el-option v-for="item in deptOption" :key="item.deptId" :value="item.deptId" :label="item.deptName"></el-option>
              </el-select>
            </template>
                  </vxe-column>
      </vxe-table>
          </el-form>

 props: {
    
    
    enterFlag: {
    
    
      type: Boolean,
      default: false,
    },
    }
watch:{
    
    
    enterFlag: {
    
    
      handler(newVal) {
    
    
        if (newVal === true) {
    
    
          this.getAllInput()
        }
      },
      deep: true,
    },
}
    // 滚动事件
    getAllInput() {
    
    
      setTimeout(() => {
    
    
        this.$nextTick(() => {
    
    
          this.$refs.vxeTable.scrollTo(0)
        })
      }, 500)
    },

最重要的是js

export function tableKeydown(e, rowIndex, columnIndex) {
    
    
  //事件对象兼容
  let ev = e || window.event || arguments.callee.caller.arguments[0]
  //通过ev 获取 当前input 名称 用于判断属于哪列
  let className = ev.target.offsetParent.className
  //获取所有input
  let inputAll = document.querySelectorAll('.mytable-scrollbar .el-input__inner')
  //获取所有索引组成的数组
  const inputIndexs = []
  for (var i = 0; i < inputAll.length; i++) {
    
    
    inputIndexs.push(i)
  }
  //按索引,增加自定义属性,这里遇到的坑是js中之前已经增加过自定义属性,代码复制过来的时候没有改,导致一直查找不到我现在增的
  //自定义属性customFocusIndex,一定要保证自定义属性的唯一性
  for (var i = 0; i < inputIndexs.length; i++) {
    
    
    inputAll[i].setAttribute('customFocusIndex', inputIndexs[i])
  }
  let attrIndex = parseInt(ev.target.getAttribute('customFocusIndex'))

  // ev.keyCode == 13代表按下的是回车,向上键33,向下键34
  if (ev && ev.keyCode == 13) {
    
    
  //每次回车+1,下一个输入框的索引
    attrIndex += 1
    //如果遇到禁用列,多加一次索引
    for (var i = attrIndex; i < inputAll.length; i++) {
    
    
      if (inputAll[i] && inputAll[i].disabled) {
    
    
        attrIndex += 1
      } else {
    
    
        break
      }
    }
    //一行一共有多少列,这里写死了
    const rowline = rowIndex * 12
//第10列之后都是禁用列,这里也写死了,第10列代表最后一列
    if (columnIndex == 10) {
    
    
      // 最后一列跳转到第一列
      inputAll[rowline].focus()
      inputAll[rowline].select()
      //调用scrollTo方法,手动将滚动条滚动到第一列
      setTimeout(() => {
    
    
        this.$nextTick(() => {
    
    
          this.$refs.vxeTable.scrollTo(0)
        })
      }, 500)
  //最后一列调整到第一列的时候要手动选中复选框,使用setCheckboxRow方法
      const vxeTable = this.$refs.vxeTable
      const data = this.$refs.vxeTable.getData()
      vxeTable.setCheckboxRow(data[rowIndex],true)
      this.$refs.vxeTable.loadData(data)
      // 下拉框点击选择,下拉框比输入框多一个类名is-focus,通过此区分
      if (className.indexOf('is-focus') != -1) {
    
    
      //动态获取el-select的实例,防止重复,行索引+列索引都拼接上了
        let DictselectRef = 'select' + columnIndex + 'input' + rowIndex
        //封装的el-select组件有两层ref,根据自己定义的ref
        if (this.$refs[DictselectRef][0].$refs.selectRef) {
    
    
          // dictSelect
          this.$refs[DictselectRef][0].$refs.selectRef.visible = false
          this.$refs[DictselectRef][0].$refs.selectRef.blur()
          this.$refs[DictselectRef][0].$refs.selectRef.$refs.reference.$refs.input.hidden = true
        } else {
    
    
          // el-select 原生el-select
          this.$refs[DictselectRef].visible = false
          this.$refs[DictselectRef][0].blur()
          this.$refs[DictselectRef][0].$refs.reference.$refs.input.hidden = true
        }
      }
      return
    }
    if (className.indexOf('el-input--suffix') != -1) {
    
    
      // 下拉框点击选择
      if (className.indexOf('is-focus') != -1) {
    
    
        let DictselectRef = 'select' + columnIndex + 'input' + rowIndex
        if (this.$refs[DictselectRef][0].$refs.selectRef) {
    
    
          // dictSelect
          this.$refs[DictselectRef][0].$refs.selectRef.visible = false
          this.$refs[DictselectRef][0].$refs.selectRef.blur()
          this.$refs[DictselectRef][0].$refs.selectRef.$refs.reference.$refs.input.hidden = true
        } else {
    
    
          // el-select
          this.$refs[DictselectRef].visible = false
          this.$refs[DictselectRef][0].blur()
          this.$refs[DictselectRef][0].$refs.reference.$refs.input.hidden = true
        }
      }
     //当第8列回车的时候,会因为滚动条遮挡看不见第九列是否光标已进入,所以加了滚动条向右滚动事件
      if (columnIndex == 8) {
    
         
        let scrollLeft = this.$refs.vxeTable.$el.scrollWidth
        setTimeout(() => {
    
    
          this.$nextTick(() => {
    
    
            this.$refs.vxeTable.scrollTo(scrollLeft)
          })
        }, 500)
      }
      if (inputAll[attrIndex]) {
    
    
        inputAll[attrIndex].focus()
      }
    }
  }
}

猜你喜欢

转载自blog.csdn.net/weixin_49668076/article/details/132629362