Bootstrap

前端elementplus实现表格点击某一行选中,按ctrl点击行多选,按shift点击行连选

前端element-plus实现表格点击某一行选中,按ctrl点击行多选,按shift点击行连选

1.实现效果

在这里插入图片描述

2.代码

框架vue3,组件库element-plus
我在这里使用el-table二次封装了一个表格组件

<template>
  <div :class="`ycd-table ${props.class}`">
    <el-table
      ref="tableRef"
      :data="props.data"
      style="width: 100%"
      class="ycd-table"
      :header-cell-style="{
        backgroundColor: '#f5f7fa',
      }"
      border
      row-key="id"
      :highlight-current-row="highlightCurrentRow"
      :row-class-name="tableRowClassName"
      @row-click="handleRowClick"
      @selection-change="handleSelectionChange"
      @current-change="handleCurrentChange"
      :height="props.height"
      table-layout="auto"
    >
      <el-table-column
        fixed="left"
        type="selection"
        width="38"
        :reserve-selection="true"
        v-if="type === 1"
        :selectable="selectableFn"
      />
      <slot></slot>
      <template #empty>
        <el-empty description="暂无数据" />
      </template>
    </el-table>
  </div>
</template>

<script setup>
import { nextTick, ref, watch } from "vue";

const props = defineProps({
  type: {
    type: Number,
    default: 0, // 0: 普通表格 1: 多选表格
  },
  data: {
    type: Array,
    default: [],
  },
  class: {
    type: String,
    default: "",
  },
  height: {
    type: String,
    default: "600",
  },
  // 高亮当前行,单选时使用
  highlightCurrentRow: {
    type: Boolean,
    default: false,
  },
  // 默认勾选
  selected: {
    type: Array,
    default: [],
  },
  // 自定义选择是否禁用
  selectableFn: {
    type: Function,
    default: () => true,
  },
  // 选中的行
  multipleSelection: {
    type: Array,
    default: [],
  },
  // 显示时用于清空选择
  show: {
    type: Boolean,
    default: false,
  },
});
const emits = defineEmits([
  "row-click",
  "selection-change",
  "current-change",
  "update:multipleSelection",
]);

const tableRef = ref();

// 定义选中行的class
const tableRowClassName = ({ row, rowIndex }) => {
  if (props.multipleSelection.some((item) => item?.id === row?.id)) {
    return "table-selected-row";
  }
  return "";
};
// *****************主要功能代码***************
// 上一次点击的索引
const lastClickIndex = ref(undefined);
// 上一次选择的方式 1:单选,2:多选,3:连选
let lastSelectStatus = 1;
// 表格行点击事件
const handleRowClick = (row, column, e) => {
  // 测试自定义禁用规则,如果这一行通过规则校验为false,说明这一行是禁止选择的,不进行勾选
  if (!props.selectableFn(row)) {
    return;
  }
  const { ctrlKey, shiftKey } = e;
  const rowIndex = props.data.findIndex((item) => item.id === row.id);
  if (!ctrlKey && !shiftKey) {
    // ctrl和shift都没有按下时单选
    // 如果上次的索引和这次的索引不同 或 上一次不是单选 则清空选择
    if (lastClickIndex.value !== rowIndex || lastSelectStatus !== 1) {
      tableRef.value.clearSelection();
    }
    tableRef.value.toggleRowSelection(row, undefined);
    lastSelectStatus = 1;
  } else if (ctrlKey) {
    // 按下ctrl多选
    tableRef.value.toggleRowSelection(row, undefined);
    lastSelectStatus = 2;
  } else if (shiftKey) {
    // 按下shift连选
    if (lastClickIndex.value === undefined) {
      return;
    }
    let minIndex = 0;
    let maxIndex = 0;
    if (rowIndex < lastClickIndex.value) {
      minIndex = rowIndex;
      maxIndex = lastClickIndex.value + 1;
    } else {
      minIndex = lastClickIndex.value;
      maxIndex = rowIndex + 1;
    }
    props.data
      .slice(minIndex, maxIndex)
      .forEach((item) => tableRef.value.toggleRowSelection(item, true));
    lastSelectStatus = 3;
  }
  lastClickIndex.value = rowIndex;
};
// 处理表格复选框改变事件
const handleSelectionChange = (val) => {
  emits("update:multipleSelection", val);
};

// 当前行变化触发(单选)
const handleCurrentChange = (val) => {
  emits("current-change", val);
};

// 默认勾选
watch(
  () => props.selected,
  async (selectedRow) => {
    if (selectedRow.length > 0) {
      await nextTick();
      tableRef.value.clearSelection();
      selectedRow.forEach((row) => {
        // 注意:这里的row的引用必须和表格行的引用相同才能生效
        // 否则无法实现选中效果
        // 也就是说这里初始选择行的数据必须从渲染表格的数据那里取
        tableRef.value.toggleRowSelection(row, undefined);
      });
    } else {
      tableRef.value.clearSelection();
    }
  }
);

// 表格出现时清空选择
watch(
  () => props.show,
  async (show) => {
    if (show) {
      await nextTick();
      tableRef.value.clearSelection();
    }
  }
);
</script>

<style scoped lang="scss">
.ycd-table {
  user-select: none;
  // 去除表格边框
  :deep(.el-table th.el-table__cell.is-leaf),
  :deep(.el-table td.el-table__cell) {
    border: 0;
  }
  // :deep(.el-table .el-table__row:not(:last-of-type) td.el-table__cell),
  :deep(.el-table .el-table__row td.el-table__cell),
  :deep(.el-table .el-table__header th.el-table__cell) {
    border-bottom: 1px solid #ebeef5;
  }
  :deep(.el-table__border-left-patch) {
    width: 0;
    background-color: transparent;
  }
  :deep(.el-table--border::after),
  :deep(.el-table--border::before),
  :deep(.el-table--border .el-table__inner-wrapper::after),
  :deep(.el-table__inner-wrapper::before) {
    width: 0;
    background-color: transparent;
  }
  // 选中行的颜色
  :deep(.el-table) .table-selected-row {
    --el-table-tr-bg-color: var(--el-color-primary-light-9);
  }
}
</style>

使用时如下
multipleSelection为多选选择的数据

<ycd-table
  :data="tableDataPage"
  v-model:multipleSelection="multipleSelection"
  v-loading="loading"
  :type="1"
  :show="tableShow"
>
	// el-table-column列配置
</ycd-table>

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;