el-table手动实现多选,全选实现的是分页全选

当前el-table的多选实现是仅仅选择当前的数据,不满足需求

当数据是一次性全部返回的时候,我们需要全选的时候选择的是全部数据,这时组件库不满足需求

复制代码
<template>
  <div class="table-container">
    <!-- 全选和操作 -->
    <div class="toolbar">
      <el-button
        type="danger"
        :disabled="selectedIds.size === 0"
        @click="handleDeleteSelected"
      >
        批量删除
      </el-button>

      <span v-if="selectedIds.size > 0" class="selection-info">
        已选择 {{ selectedIds.size }} 项
      </span>
    </div>

    <el-table
      :data="paginatedData"
      style="width: 100%"
      v-loading="loading"
      row-key="id"
      ref="tableRef"
    >
      <!-- 手动多选列 -->
      <el-table-column width="55" align="center">
        <template #header>
          <el-checkbox
            v-model="allSelected"
            :indeterminate="isIndeterminate"
            @change="handleSelectAll"
          />
        </template>
        <template #default="{ row }">
          <el-checkbox v-model="row.selected" @change="handleRowSelect(row)" />
        </template>
      </el-table-column>

      <el-table-column prop="id" label="ID" width="80" />
      <el-table-column prop="name" label="姓名" width="120" />
      <el-table-column prop="age" label="年龄" width="80" />
      <el-table-column prop="email" label="邮箱" />
      <el-table-column prop="address" label="地址" />
      <el-table-column prop="createTime" label="创建时间" width="180" />
    </el-table>

    <el-pagination
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="currentPage"
      :page-sizes="[10, 20, 50, 100]"
      :page-size="pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total"
      background
      style="margin-top: 20px; text-align: right"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed, nextTick, onMounted } from "vue";
import type { TableInstance } from "element-plus";
import { ta } from "element-plus/es/locales.mjs";

interface User {
  id: number;
  name: string;
  age: number;
  email: string;
  address: string;
  createTime: string;
  selected?: boolean;
}

const tableData = ref<User[]>([]);
const tableRef = ref<TableInstance>();
const loading = ref(false);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(100);

const selectedIds = ref<Set<number>>(new Set());

// 分页数据
const paginatedData = computed(() => {
  const start = (currentPage.value - 1) * pageSize.value;
  const end = start + pageSize.value;
  return tableData.value.slice(start, end);
});

// 全选/半选状态
const allSelected = ref(false);
const isIndeterminate = computed(() => {
  return selectedIds.value.size > 0 && selectedIds.value.size < total.value;
});

const updateSelectStatus = () => {
  const currentPageIds = paginatedData.value.map((i) => i.id);
  const selectedCount = currentPageIds.filter((id) =>
    selectedIds.value.has(id)
  ).length;
  allSelected.value =
    selectedCount === currentPageIds.length && currentPageIds.length > 0;
};

// 行选中变化
const handleRowSelect = (row: User) => {
  if (row.selected) selectedIds.value.add(row.id);
  else selectedIds.value.delete(row.id);
  updateSelectStatus();
};

// 全选/取消全选当前页
const handleSelectAll = (val: boolean) => {
  if (val) {
    tableData.value.forEach((item) => {
      selectedIds.value.add(item.id);
      item.selected = true;
    });
  } else {
    selectedIds.value.clear();
    tableData.value.forEach((item) => {
      item.selected = false;
    });
  }
  updateSelectStatus();
};

// 分页切换时更新行选中状态
const updateCurrentPageSelection = () => {
  nextTick(() => {
    paginatedData.value.forEach((item) => {
      item.selected = selectedIds.value.has(item.id);
    });
    updateSelectStatus();
  });
};

// 分页事件
const handleSizeChange = (val: number) => {
  pageSize.value = val;
  currentPage.value = 1;
  updateCurrentPageSelection();
};

const handleCurrentChange = (val: number) => {
  currentPage.value = val;
  updateCurrentPageSelection();
};

// 批量删除
const handleDeleteSelected = () => {
  tableData.value = tableData.value.filter(
    (item) => !selectedIds.value.has(item.id)
  );
  selectedIds.value.clear();
  updateCurrentPageSelection();
  total.value = tableData.value.length;
};

// 模拟数据
const generateMockData = (count: number) => {
  const data: User[] = [];
  for (let i = 1; i <= count; i++) {
    data.push({
      id: i,
      name: `用户${i}`,
      age: Math.floor(Math.random() * 50) + 18,
      email: `user${i}@example.com`,
      address: `北京市朝阳区街道${i}号`,
      createTime: new Date(
        Date.now() - Math.floor(Math.random() * 7 * 24 * 60 * 60 * 1000)
      ).toLocaleDateString(),
    });
  }
  return data;
};

const getTableData = () => {
  loading.value = true;
  setTimeout(() => {
    tableData.value = generateMockData(total.value);
    updateCurrentPageSelection();
    loading.value = false;
  }, 300);
};

onMounted(() => {
  getTableData();
});
</script>

<style scoped>
.table-container {
  padding: 20px;
}

.toolbar {
  margin-bottom: 15px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.selection-info {
  color: #606266;
  font-size: 14px;
}
</style>
相关推荐
英俊潇洒美少年1 小时前
React 实现 AI 流式打字机对话:SSE 分包粘包处理 + 并发优化
前端·javascript·react.js
叫我一声阿雷吧2 小时前
JS 入门通关手册(44):宏任务 / 微任务 / Event Loop(前端最难核心,面试必考
javascript·宏任务·event loop· 前端面试· 微任务· 事件循环·js单线程
We་ct2 小时前
LeetCode 172. 阶乘后的零:从暴力到最优,拆解解题核心
开发语言·前端·javascript·算法·leetcode·typescript
军军君012 小时前
数字孪生监控大屏实战模板:可视化数字统计展示
前端·javascript·vue.js·typescript·echarts·数字孪生·前端大屏
吴声子夜歌2 小时前
ES6——Iterator和for...of循环详解
前端·javascript·es6
小李子呢02112 小时前
前端八股3---ref和reactive
开发语言·前端·javascript
踩着两条虫2 小时前
VTJ.PRO AI + 低代码实战:接入高德地图
前端·vue.js·ai编程
xiaotao1313 小时前
Vite 与 Webpack 开发/打包时环境变量对比
前端·vue.js·webpack
web_小码农3 小时前
CSS 3D动画 旋转木马示例(带弧度支持手动拖动)
javascript·css·3d
Armouy3 小时前
Electron:核心概念、性能优化与兼容问题
前端·javascript·electron