vue3 + element-plus el-table表格二次封装,支持复选框,排序,分页。

一、customTable.vue组件

xml 复制代码
<template>
  <div class="columns-table">
    <div class="columns-table-main">
      <!-- 表格 -->
      <el-table
        ref="tableRef"
        row-key="id"
        show-overflow-tooltip
        tooltip-effect="dark"
        :data="tableData"
        v-loading="loading"
        :element-loading-text="loadingText"
        :border="tableBorder"
        :height="tableHeight"
        :max-height="tableMaxHeight"
        :empty-text="emptyText"
        :empty-image="emptyImage"
        :default-expand-all="defaultExpandAll"
        @selection-change="selectionChange"
        @sort-change="sortChange"
        :header-cell-style="{
          'fond-size': '14px',
          height: '40px',
          'font-weight': 'bold',
          color: 'rgba(0,0,0,0.85)',
          'background-color': '#F5F5F5',
          'text-align': 'center',
        }"
        :cell-style="{
          'text-align': 'center',
          height: '40px',
          color: 'rgba(0,0,0,0.85)',
        }"
      >
        <!-- 复选框 -->
        <el-table-column
          v-if="selection"
          :reserve-selection="reserveSelection"
          type="selection"
          width="45"
          fixed
          align="center"
        >
        </el-table-column>
        <!-- 序号 -->
        <el-table-column
          v-if="showIndex"
          type="index"
          label="序号"
          width="60"
          align="center"
        >
          <template #default="scope">
            <span>{{ (currentPage - 1) * pageSize + scope.$index + 1 }}</span>
          </template>
        </el-table-column>
        <!-- 其他列 -->
        <template v-for="(item, index) in columns">
          <template v-if="item.type === 'slot'">
            <el-table-column
              :key="index"
              :width="item.width"
              :min-width="item.minWidth"
              :prop="item.prop"
              :label="item.label"
              :align="item.align ? item.align : 'left'"
              :fixed="item.fixed ? item.fixed : false"
            >
              <!-- 操作列 -->
              <template #default="scope" v-if="item.slotType == 'options'">
                <el-button
                  type="primary"
                  text
                  v-for="(btn, ind) in item.buttons"
                  :key="ind"
                  :disabled="scope.row[btn.key]"
                  @click="btn.fn(scope.row)"
                  >{{ btn.text }}</el-button
                >
              </template>
              <!-- 普通自定义列 -->
              <template #default="scope" v-else>
                <slot
                  :name="item.slotType"
                  :row="scope.row"
                  :label="item.label"
                  :index="scope.$index"
                  :column="scope.column"
                />
              </template>
            </el-table-column>
          </template>
          <!--普通表格-->
          <template v-else>
            <el-table-column
              :key="index"
              :sortable="item.sortable"
              :prop="item.prop"
              :label="item.label"
              :width="item.width"
              :min-width="item.minWidth || '80'"
              :align="item.align ? item.align : 'left'"
              :fixed="item.fixed ? item.fixed : false"
            ></el-table-column>
          </template>
        </template>
      </el-table>
    </div>
    <div class="columns-table-page">
      <!-- 分页 -->
      <el-pagination
        v-if="showPagination"
        :total="total"
        v-model:current-page="currentPage"
        v-model:page-size="pageSize"
        :page-sizes="pageSizes"
        background
        layout="total, sizes, prev, pager, next, jumper"
      ></el-pagination>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
let loading = defineModel("loading");
const emits = defineEmits(["selection-change", "sort-change"]);
const props = defineProps({
  // 分页
  total: {
    type: Number,
    default: 0,
  },
  pageSizes: {
    type: Array,
    default: () => [10, 20, 50, 100],
  },
  // 表格
  tableData: {
    type: Array,
    default: () => [],
  },
  columns: {
    type: Array,
    default: () => [],
  },
  loadingText: {
    type: String,
    default: () => "正在加载中...",
  },
  tableBorder: {
    type: Boolean,
    default: () => true,
  },
  tableHeight: {
    type: String,
    default: () => "auto",
  },
  tableMaxHeight: {
    type: String,
    default: () => "500px",
  },
  emptyText: {
    type: String,
    default: () => "暂无数据",
  },
  emptyImage: {
    type: String,
    default: () => "https://img.yzcdn.cn/vant/empty-block.png",
  },
  showSummary: {
    type: Boolean,
    default: () => true,
  }, // 是否显示合计行
  defaultExpandAll: {
    type: Boolean,
    default: () => false,
  },
  // 是否显示复选框
  selection: {
    type: Boolean,
    default: () => true,
  },
  // 分页的时候是否记录选择复选框状态
  reserveSelection: {
    type: Boolean,
    default: () => true,
  },
  // 是否展示翻页组件
  showPagination: {
    type: Boolean,
    default: () => true,
  },
  // 是否展示序号
  showIndex: {
    type: Boolean,
    default: () => false,
  },
  // 默认选中的数据
  checkData: {
    type: Array,
    default: () => [],
  },
});
const tableRef = ref(null);
const selectionChange = (info) => {
  emits("selection-change", info);
};
const sortChange = (info) => {
  emits("sort-change", info);
};
// 外面监听pageSize和currentPage变化,调用查询数据接口。
const pageSize = defineModel("pageSize");
const currentPage = defineModel("currentPage");
onMounted(() => {
  // 复选框默认选中行
  if (props.checkData.length > 0) {
    props.tableData.forEach((item) => {
      if (props.checkData.includes(item.id)) {
        tableRef.value.toggleRowSelection(item, true);
      } else {
        tableRef.value.toggleRowSelection(item, false);
      }
    });
  } else {
    tableRef.value.clearSelection();
  }
});
// 清除选中状态
const clearSelectionFun = () => {
  tableRef.value.clearSelection();
};
defineExpose({
  clearSelectionFun,
});
</script>
<style lang="scss" scoped>
.columns-table {
  display: flex;
  flex-direction: column;
  .columns-table-main {
    margin-bottom: 12px;
  }
  .columns-table-page {
    display: flex;
    justify-content: flex-end;
  }
}
</style>

二、Home.vue 调用示例

ini 复制代码
<template>
  <custom-table
    v-model:loading="loading"
    :tableData="tableData"
    :columns="columns"
    :showIndex="tableConfig.showIndex"
    :selection="tableConfig.selection"
    :tableHeight="tableConfig.tableHeight"
    v-model:pageSize="pageConfig.pageSize"
    v-model:currentPage="pageConfig.currentPage"
    :total="pageConfig.total"
    :pageSizes="pageConfig.pageSizes"
    @sort-change="sortChange"
    @selection-change="selectionChange"
  >
    <!-- 自定义插槽,可以将插槽中的数据传递出来,status插槽名称,info slot中配置的属性 -->
    <template v-slot:status="info">
      {{ info.row.status ? "成功" : "成功" }}
    </template>
  </custom-table>
</template>
<script setup>
import CustomTable from "../components/CustomTable.vue";
import { ref, watch, watchEffect, reactive } from "vue";
// 表格相关
// 可以写在hooks里面
const loading = ref(false); // useHooks
const tableConfig = ref({
  showIndex: true,
  selection: true,
  tableHeight: "calc(100vh - 300px)",
});
const tableData = ref([]); // useHooks
const editFun = (row) => {};
const deleteFun = (row) => {};
const columns = ref([
  {
    label: "姓名",
    prop: "name",
    sortable: true,
  },
  {
    label: "年龄",
    prop: "age",
    sortable: true,
  },
  {
    label: "性别",
    prop: "sex",
  },
  {
    label: "自定义",
    type: "slot",
    slotType: "status",
    width: 100,
  },
  {
    label: "操作",
    type: "slot", // 插槽
    slotType: "options", // 自定义内容
    width: 160,
    buttons: [
      {
        text: "编辑",
        fn: editFun,
      },
      {
        text: "删除",
        fn: deleteFun,
      },
    ],
  },
]);
const getTableData = () => {
  loading.value = true;
  setTimeout(() => {
    tableData.value = [
      {
        id: 1,
        name: "zxc",
        age: 34,
        sex: "男",
        status: 1,
        address: "北京",
      },
      {
        id: 2,
        name: "zxc",
        age: 34,
        sex: "男",
        status: 0,
        address: "北京",
      },
    ];
    loading.value = false;
  }, 1000);
};
const sortChange = (info) => {}; // 排序
const selectionChange = (info) => {}; // 选择
// 分页相关
const pageConfig = reactive({
  pageSize: 10,
  currentPage: 1,
  total: 120,
  pageSizes: [10, 20, 50, 100],
});
watch(
  [() => pageConfig.currentPage, () => pageConfig.pageSize],
  ([newCurrent, newSize], [oldCurrent, oldSize]) => {
    if (newCurrent !== oldCurrent || newSize !== oldSize) {
      getTableData();
    }
  },
  {
    immediate: true,
  }
);
</script>
相关推荐
当牛作馬38 分钟前
React——ant-design组件库使用问题记录
前端·react.js·前端框架
0wioiw01 小时前
Flutter基础(前端教程⑨-图片)
前端·flutter
一只一只妖1 小时前
uniapp小程序无感刷新token
前端·小程序·uni-app
武昌库里写JAVA1 小时前
vue3面试题(个人笔记)
java·vue.js·spring boot·学习·课程设计
绅士玖1 小时前
📝 深入浅出 JavaScript 拷贝:从浅拷贝到深拷贝 🚀
前端
中微子1 小时前
闭包面试宝典:高频考点与实战解析
前端·javascript
brzhang2 小时前
前端死在了 Python 朋友的嘴里?他用 Python 写了个交互式数据看板,着实秀了我一把,没碰一行 JavaScript
前端·后端·架构
G等你下课2 小时前
告别刷新就丢数据!localStorage 全面指南
前端·javascript
该用户已不存在2 小时前
不知道这些工具,难怪的你的Python开发那么慢丨Python 开发必备的6大工具
前端·后端·python