vue2 el-table中的多选框按住shift键连选,可反选,可以间隔选

288 阅读2分钟

效果如图: image.png

原始版本代码

<template>
  <div>
    <el-table
      :data="tableData"
      tooltip-effect="dark"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      @select="pinSelect"
      ref="multipleTable"
    >
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column label="日期" width="120">
        <template slot-scope="scope">{{ scope.row.date }}</template>
      </el-table-column>
      <el-table-column prop="name" label="姓名" width="120"></el-table-column>
      <el-table-column prop="address" label="地址" show-overflow-tooltip></el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  name: 'ElementTable',
  data() {
    return {
      origin: -1, // 这里给一个变量作为起点
      pin: false, // 这里给一个变量,默认为false,不按住
      tableData: [
        {
          date: '2016-05-01',
          name: '王幼虎',
          address: '上海市普陀区金沙江路 1511 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1512 弄'
        },
        {
          date: '2016-05-03',
          name: '王中虎',
          address: '上海市普陀区金沙江路 1513 弄'
        },
        {
          date: '2016-05-04',
          name: '王老虎',
          address: '上海市普陀区金沙江路 1514 弄'
        },
        {
          date: '2016-05-05',
          name: '王死虎',
          address: '上海市普陀区金沙江路 1515 弄'
        },
        {
          date: '2016-05-06',
          name: '王骨虎',
          address: '上海市普陀区金沙江路 1516 弄'
        },
        {
          date: '2016-05-07',
          name: '王灰虎',
          address: '上海市普陀区金沙江路 1517 弄'
        }
      ],
      multipleSelection: [],
      selected: false,
      lists: []
    }
  },
  watch: {
    lists(val) {
      console.log('选中的数据', val)
    }
  },
  methods: {
    handleSelectionChange(val) {
      this.multipleSelection = val
      // console.log('选中数据', this.multipleSelection)
    },
    pinSelect(item, row) {
      let selected = item.length && item.indexOf(row) !== -1
      this.selected = selected 
      const data = this.$refs.multipleTable.tableData
      const origin = this.origin
      const endIdx = row.index
      if (this.selected) {
        if (this.pin && item.includes(data[origin])) {
          const sum = Math.abs(origin - endIdx) + 1
          const min = Math.min(origin, endIdx)
          let i = 0
          while (i < sum) {
            const index = min + i
            this.$refs.multipleTable.toggleRowSelection(data[index], true)
            i++
            const isIndexExists = this.lists.some(item => item.index === index)
            if (!isIndexExists) {
              this.lists.push(data[index])
            }
          }
        } else {
          this.origin = row.index
          this.lists = item
        }
      } else {
        if (!this.pin && !item.includes(data[origin])) {
          const sum = Math.abs(origin - endIdx) + 1
          const min = Math.min(origin, endIdx)
          let i = 0
          while (i < sum) {
            const index = min + i
            this.$refs.multipleTable.toggleRowSelection(data[index], false)
            i++
            this.lists = this.lists.filter(item => item.index !== index)
          }
        } else {
          this.origin = row.index
          this.lists = item
        }
      }
    }
  },
  // 这里是获取键盘事件
  mounted() {
    window.addEventListener('keydown', code => {
      if (code.keyCode === 16 && code.shiftKey) {
        if (this.selected) {
          this.pin = true
        } else {
          this.pin = false
        }
      }
    })
    window.addEventListener('keyup', code => {
      if (code.keyCode === 16) {
        if (this.selected) {
          this.pin = false
        } else {
          this.pin = true
        }
      }
    })
  },
  created() {
    this.tableData.forEach((item, index) => {
      // 遍历索引,赋值给data数据
      item.index = index
    })
  }
}
</script>
<style scoped></style>

以上写法问题:

1、使用shift连选会导致@selection-change选中数据不对

当你按住 Shift 键连选多行时,如果手动调用了 toggleRowSelection 来选中多行,会导致 @selection-change 事件触发多次,因此 @selection-change 中的选中数据可能不准确。 可以使用 @select-all@select 事件,结合手动管理选中行状态。 我这里通过属性selectionChangeAllowed: true, // 控制 @selection-change 触发

2、各种连选,取消反选的bug ex:从1连选到10,再从10取消连选到2,点2再点8,不按住shift也会从2连选到8;点击全选,再点击3会出现1到3连选取消;从1连选到10,单选取消4是单个取消,再点8,会连选取消到8。

解决以上bug的完整代码如下:

<template>
  <div>
    <el-table
      ref="multipleTable"
      :data="tableData"
      tooltip-effect="dark"
      style="width: 100%"
      @selection-change="handleSelectionChange"
      @select="pinSelect"
    >
      <el-table-column type="selection" width="55"></el-table-column>
      <el-table-column label="日期" width="120">
        <template slot-scope="scope">{{ scope.row.date }}</template>
      </el-table-column>
      <el-table-column prop="name" label="姓名" width="120"></el-table-column>
      <el-table-column prop="address" label="地址" show-overflow-tooltip></el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  name: 'ElementTable',
  data() {
    return {
      origin: -1, // 这里给一个变量作为起点
      pin: false, // 这里给一个变量,默认为false,不按住
      tableData: [
        {
          date: '2016-05-01',
          name: '王幼虎',
          address: '上海市普陀区金沙江路 1511 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1512 弄'
        },
        {
          date: '2016-05-03',
          name: '王中虎',
          address: '上海市普陀区金沙江路 1513 弄'
        },
        {
          date: '2016-05-04',
          name: '王老虎',
          address: '上海市普陀区金沙江路 1514 弄'
        },
        {
          date: '2016-05-05',
          name: '王死虎',
          address: '上海市普陀区金沙江路 1515 弄'
        },
        {
          date: '2016-05-06',
          name: '王骨虎',
          address: '上海市普陀区金沙江路 1516 弄'
        },
        {
          date: '2016-05-07',
          name: '王灰虎',
          address: '上海市普陀区金沙江路 1517 弄'
        }
      ],
      multipleSelection: [],
      selected: false,
      lists: [],
      selectionChangeAllowed: true // 控制 @selection-change 触发
    }
  },
  watch: {
    lists(val) {
      console.log('选中的数据', val)
    }
  },
  methods: {
    handleSelectionChange(val) {
      if (this.selectionChangeAllowed) {
        this.multipleSelection = val
        // console.log('选中数据', this.multipleSelection)
      }
    },
    // 这里是select事件开始
    pinSelect(item, row) {
      let selected = item.length && item.indexOf(row) !== -1
      this.selected = selected // 取消还是选中

      const data = this.$refs['multipleTable'].tableData
      const origin = this.origin
      const endIdx = row.index
      if (this.selected) {
        // 是否按shift的
        if (this.pin && item.includes(data[origin])) {
          // 当按住shift 进行连选时,暂时禁用 @selection-change 事件触发
          this.selectionChangeAllowed = false
          const sum = Math.abs(origin - endIdx) + 1
          const min = Math.min(origin, endIdx)
          let i = 0
          while (i < sum) {
            const index = min + i
            this.$refs['multipleTable'].toggleRowSelection(data[index], true)
            i++
            // console.log('返回', data[index])
            // 检查 arr 数组中是否存在相同 index 的元素
            const isIndexExists = this.lists.some(item => item.index === index)
            if (!isIndexExists) {
              this.lists.push(data[index])
            } else {
              console.log('index 已存在,不添加', data[index])
            }
          }
          // 连选操作结束,手动更新
          this.$nextTick(() => {
            this.selectionChangeAllowed = true
            this.rollSelectionChange(this.lists)
          })
        } else {
          this.origin = row.index
          this.lists = item
        }
      } else {
        // console.log('取消选中')
        // 是否按shift
        if (this.pin && !item.includes(data[origin])) {
          this.selectionChangeAllowed = false
          const sum = Math.abs(origin - endIdx) + 1
          const min = Math.min(origin, endIdx)
          let i = 0
          while (i < sum) {
            const index = min + i
            this.$refs['multipleTable'].toggleRowSelection(data[index], false)
            i++
            // 使用 filter 删除匹配的元素
            this.lists = this.lists.filter(item => item.index !== index)
          }
          // 连选操作结束,手动更新 selectedRows
          this.$nextTick(() => {
            this.selectionChangeAllowed = true // 恢复 @selection-change 事件
            this.rollSelectionChange(this.lists)
          })
        } else {
          this.origin = row.index
          this.lists = item
        }
      }
    }
  },
  // 这里是获取键盘事件
  mounted() {
    window.addEventListener('keydown', code => {
      if (code.keyCode === 16 && code.shiftKey) {
        this.pin = true
      } else {
        this.pin = false
      }
    })
    window.addEventListener('keyup', code => {
      if (code.keyCode === 16 && code.shiftKey) {
        this.pin = true
      } else {
        this.pin = false
      }
    })
  },
  created() {
    this.tableData.forEach((item, index) => {
      // 遍历索引,赋值给data数据
      item.index = index
    })
  }
}
</script>
<style scoped></style>


本文是基于参考文章写的,如果对你有帮助,留下小心心~

本文参考文章