Vue 3 + Element Plus 实现可扩展的表格组件

在开发过程中,我们经常需要创建具有复杂功能的表格组件。本文将介绍如何使用 Vue 3 和 Element Plus 库来构建一个可扩展的表格组件,包括增加自定义插槽、操作栏按钮以及动态列内容。

组件概述

我们将创建一个名为 Table 的 Vue 组件,该组件具有以下特性:

  • 支持固定表头和边框
  • 可配置的列宽和最小列宽
  • 支持树形数据和展开行
  • 自定义列内容和操作栏按钮
  • 动态计算表格高度

以下是组件的实现步骤和代码示例。

组件实现

1. 组件模板

首先,我们定义组件的模板部分,使用 el-table 组件来创建表格,并添加三个自定义插槽:defaultoperationsStartoperationsEnd

html 复制代码
<template>
  <el-table
    border
    stripe
    :data="data"
    :height="tableHeight"
    table-layout="fixed"
    :row-key="rowKey"
    :highlight-current-row="highlightCurrentRow"
    :tree-props="treeProps"
    :header-cell-style="{
      background: '#f5f7fa',
      color: '#606266',
      fontSize: '13px',
    }"
    @selection-change="handleSelectionChange"
    :v-loading="loading"
    :row-class-name="tableRowClassName"
    :expand-row-keys="expandRowKeys"
    class="absolute"
  >
    <el-table-column
      v-if="showSelection"
      align="center"
      fixed
      type="selection"
      width="55"
    />
    <el-table-column
      v-for="tableModel in tableModels"
      :key="tableModel.key"
      :prop="tableModel.key"
      :label="tableModel.label"
      :width="tableModel.width"
      :min-width="tableModel.minWidth"
      :align="tableModel.align"
      :header-align="tableModel.headerAlign"
    >
      <template #default="{ row }">
        <slot
          v-if="slots[tableModel.key]"
          :name="tableModel.key"
          v-bind="row"
        ></slot>
        <span v-else>
          {{ row[tableModel.key] }}
        </span>
      </template>
    </el-table-column>
    <el-table-column
      fixed="right"
      label="操作"
      :width="operationsWidth"
      :min-width="minWidth"
      :align="operationsAlign"
      :header-align="operationsHeaderAlign"
      v-if="shoeOperations"
    >
      <template #default="{ row, $index }">
        <slot name="operationsStart" v-bind="row"></slot>
        <el-button
          link
          type="primary"
          :icon="Edit"
          @click="handleEdit(row, $index)"
          :v-hasPerm="editHasPerm"
          v-if="showEditBtn"
          >编辑
        </el-button>
        <el-button
          @click="handleDelete(row, $index)"
          link
          type="danger"
          :icon="Delete"
          v-if="showDeleteBtn"
          :v-hasPerm="deleteHasPerm"
          >删除
        </el-button>
        <slot name="operationsEnd" v-bind="row"></slot>
      </template>
    </el-table-column>
  </el-table>
</template>

2. 组件脚本

在脚本部分,我们定义了组件的 props、slots 和 emits,以及一些必要的响应式数据和函数。

javascript 复制代码
<script setup lang="ts">
// 使用 unknown 类型表示任何值
type MyUnknownType = unknown;
/*
 * @slot default 插槽
 * operationsStart 从操作栏开头增加按钮
 * operationsStart 从操作栏结尾增加按钮
 * Key TableModel里面的Key,用来自定义列的内容
 * */

import { Delete, Edit } from '@element-plus/icons-vue';

// 【接口】table表格模型
interface ITableModel {
  // 表头显示文字
  label: string;
  // 表格列Keu
  key: string;
  // 表格列宽
  width?: number | string;
  // 表格列显示文字
  value?: string;
  // 表格内容位置
  align?: string;
  // 表格表头位置
  headerAlign?: string;
  // 列最小宽度
  minWidth?: number | string;
}

// 【接口】接受传参字段
interface IProps {
  // 生成表格参数
  tableModels?: ITableModel[];
  // 表格数据
  data: object[];
  // 是否为树形表格
  treeProps?: unknown;
  // 加载状态
  loading?: boolean;
  // 表格行Keu
  rowKey?: any;
  // 表格高度
  height?: any;
  // 表格自适应减去的高度
  // eslint-disable-next-line vue/prop-name-casing
  dynamic?: number;
  // 表格单选
  highlightCurrentRow?: boolean;
  // 是否显示编辑按钮
  showEditBtn?: boolean;
  // 是否显示多选框
  showSelection?: boolean;
  // 是否显示删除按钮
  showDeleteBtn?: boolean;
  // 删除权限
  deleteHasPerm?: any;
  // 编辑权限
  editHasPerm?: any;
  // 操作栏宽度
  operationsWidth?: number;
  // 是否显示操作栏
  shoeOperations?: boolean;
  // 操作栏内容位置
  operationsAlign?: string;
  // 操作栏头部位置
  operationsHeaderAlign?: string;
  // 操作栏最小宽度
  minWidth?: number | string;
  // 树表格时默认展开参数
  expandRowKeys?: any;
}

// 【接口】分页
interface IPage {
  // 当前页
  pageNum: number;
  // 每页显示条数
  pageSize: number;
}

// 初始化默认参数
const props = withDefaults(defineProps<IProps>(), {
  loading: false,
  rowKey: 'id',
  dynamic: 0,
  showEditBtn: true,
  highlightCurrentRow: false,
  showSelection: false,
  showDeleteBtn: true,
  deleteHasPerm: [],
  editHasPerm: [],
  tableModels: () => [],
  treeProps: () => [],
  shoeOperations: true,
  operationsWidth: 150,
  operationsAlign: 'center',
  operationsHeaderAlign: 'center',
  expandRowKeys: [],
  minWidth: 'auto',
  height: 'auto'
});

// 插槽对象
const slots = useSlots();

// 定义组件发出的事件
const emit = defineEmits(['handleEdit', 'handleDelete', 'handlePagination', 'handleSelectionChange', 'handlePage']);

const tableHeight = ref<any>();

// watch监听动态计算table的高度,根据dynamic是否大于0,dynamic层级大于height
watch(
  [() => props.height, () => props.dynamic],
  ([height, dynamic]) => {
    if (height && !dynamic) {
      tableHeight.value = height;
    } else if ((!height && dynamic) || (height && dynamic)) {
      // 获取浏览器窗口高度
      const windowHeight = window.innerHeight;
      tableHeight.value = windowHeight - dynamic;
    } else {
      tableHeight.value = 'auto';
    }
  },
  {
    deep: true,
    immediate: true
  }
);

// 增加样式Class
const tableRowClassName = ({ row, rowIndex }: { row: Object; rowIndex: number }) => {
  if (rowIndex % 2 === 1) {
    return 'warning-row';
  }
  return '';
};

// 点了多选
const handleSelectionChange = (val: ITableModel[]) => {
  emit('handleSelectionChange', val);
};

// 点击了编辑
const handleEdit = (data: object, index: number) => {
  emit('handleEdit', data, index);
};

// 点击了删除
const handleDelete = (data: object, index: number) => {
  emit('handleDelete', data, index);
};
</script>

3. 使用案例

以下是如何在父组件中使用 Table 组件的示例,包括自定义列内容和操作栏按钮。

html 复制代码
<Table
  :dynamic="dynamicHeight"
  :tableModels="table.tableModels"
  :data="table.data"
  :loading="table.loading"
  :showSelection="table.showSelection"
  :operationsWidth="table.operationsWidth"
  @handle-edit="openDialog"
  @handle-delete="handleDelete"
  @handle-selection-change="handleSelectionChange"
>
  <!-- 自定义列【status】字段 -->
  <template #status="scope">
    <el-tag size="small" v-if="scope.status === 1" type="success">
      正常
    </el-tag>
    <el-tag size="small" v-else type="info">禁用</el-tag>
  </template>
  <!-- 操作栏从开头增加按钮 -->
  <template #operationsStart="scope">
    <el-button
      type="primary"
      size="small"
      link
      :icon="Position"
      @click="openMenuDialog(scope)"
    >
      分配权限
    </el-button>
  </template>
</Table>

4. 渲染表格的数据

最后,我们定义表格的列模型和数据。

javascript 复制代码
const table = reactive({
  tableModels: [
    {
      label: "角色名称",
      key: "name",
      align: "left",
      headerAlign: "center",
      value: "name",
      minWidth: 200,
    },
    {
      label: "角色编码",
      key: "code",
      width: 200,
      align: "center",
      headerAlign: "center",
      value: "code",
    },
    {
      label: "数据权限",
      key: "dataScope",
      width: 200,
      align: "center",
      headerAlign: "center",
      value: "dataScope",
    },
    {
      label: "状态",
      key: "status",
      width: 200,
      align: "center",
      headerAlign: "center",
      value: "status",
    },
    {
      label: "排序",
      key: "sort",
      width: 200,
      align: "center",
      headerAlign: "center",
      value: "sort",
    },
  ],
  data: [] as any[],
  showSelection: true,
  operationsWidth: 250,
  loading: false,
});

结论

通过以上步骤,我们创建了一个功能丰富、可扩展的表格组件。这个组件可以根据实际需求调整列配置、操作按钮和自定义内容,非常适合在复杂的应用场景中使用。希望这篇文章能帮助您在项目中更好地利用 Vue 3 和 Element Plus 库。如果您有任何疑问或建议,欢迎在评论区留言交流。

相关推荐
桂月二二34 分钟前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062062 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb2 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角2 小时前
CSS 颜色
前端·css
浪浪山小白兔3 小时前
HTML5 新表单属性详解
前端·html·html5
lee5763 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579653 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter
limit for me4 小时前
react上增加错误边界 当存在错误时 不会显示白屏
前端·react.js·前端框架
浏览器爱好者4 小时前
如何构建一个简单的React应用?
前端·react.js·前端框架
qq_392794484 小时前
前端缓存策略:强缓存与协商缓存深度剖析
前端·缓存