Vue3表格(Table)

简介: 这是一个基于 Vue2 的表格组件,支持自定义列配置、数据绑定、加载中提示、空状态提示及分页功能。主要属性包括表格列配置 `columns`、数据源 `dataSource`、加载状态 `loading` 及分页配置等。组件内置了 Spin、Empty 和 Pagination 等子组件以实现丰富的交互效果。通过简单的属性绑定即可实现数据展示、加载动画和无数据提示等功能。

可自定义设置以下属性:

  • 表格列的配置项(columns),类型:Array<{title?: string, width?: number | string, dataIndex: string, slot?: string}>,默认 []

  • 表格数据数组(dataSource),类型:any[],默认 []

  • 是否加载中(loading),类型:boolean,默认 false

  • Spin 组件属性配置,参考 Spin Props,用于配置数据加载中样式(spinProps),类型:object,默认 {}

  • Empty 组件属性配置,参考 Empty Props,用于配置暂无数据样式(emptyProps),类型:object,默认 {}

  • 是否显示分页(showPagination),类型:boolean,默认 false

  • Pagination 组件属性配置,参考 Pagination Props,用于配置分页功能(pagination),类型:Pagination,默认 {}

效果如下图:在线预览

展示数据样式:

加载中样式:

无数据样式:

①创建表格组件Table.vue:

<script setup lang="ts">
import Spin from '../spin'
import Empty from '../empty'
import Pagination from '../pagination'
interface Column {
  title?: string // 列头显示文字
  width?: number | string // 列宽度,单位 px
  dataIndex: string // 列数据字符索引
  slot?: string // 列插槽名称索引
}
interface Props {
  columns?: Column[] // 表格列的配置项
  dataSource?: any[] // 表格数据数组
  loading?: boolean // 是否加载中
  spinProps?: object // Spin 组件属性配置,参考 Spin Props,用于配置数据加载中样式
  emptyProps?: object // Empty 组件属性配置,参考 Empty Props,用于配置暂无数据样式
  showPagination?: boolean // 是否显示分页
  pagination?: object // Pagination 组件属性配置,参考 Pagination Props,用于配置分页功能
}
withDefaults(defineProps<Props>(), {
  columns: () => [],
  dataSource: () => [],
  loading: false,
  spinProps: () => ({}),
  emptyProps: () => ({}),
  showPagination: true,
  pagination: () => ({})
})
const emit = defineEmits(['change'])
function onChange(page: number, pageSize: number) {
  // 分页回调
  emit('change', page, pageSize)
}
</script>
<template>
  <div class="m-table-wrap">
    <table class="m-table">
      <thead>
        <tr class="table-tr">
          <th
            class="table-th"
            :style="`width: ${typeof item.width === 'number' ? item.width + 'px' : item.width};`"
            v-for="(item, index) in columns"
            :key="index"
          >
            {
  { item.title }}
          </th>
        </tr>
      </thead>
      <tbody class="m-table-body">
        <tr class="table-loading" v-show="loading">
          <Spin class="loading" size="small" :colspan="columns.length" v-bind="spinProps" />
        </tr>
        <tr class="table-empty-wrap" v-show="!dataSource.length">
          <td class="table-empty" :colspan="columns.length">
            <Empty class="empty" image="outlined" v-bind="emptyProps" />
          </td>
        </tr>
        <tr class="table-tr" v-for="(data, index) in dataSource" :key="index">
          <td class="m-td" v-for="(col, n) in columns" :key="n" :title="data[col.dataIndex as any]">
            <slot v-if="col.slot" v-bind="data" :name="col.slot" :index="index">{
  {
              data[col.dataIndex as any] || '--'
            }}</slot>
            <span v-else>{
  { data[col.dataIndex as any] || '--' }}</span>
          </td>
        </tr>
      </tbody>
    </table>
    <Pagination v-if="showPagination" class="mt16" @change="onChange" v-bind="pagination" />
  </div>
</template>
<style lang="less" scoped>
.m-table-wrap {
  color: rgba(0, 0, 0, 0.65);
  font-size: 14px;
  line-height: 1.5714285714285714;
  border-radius: 8px 8px 0 0;
  .m-table {
    display: table;
    table-layout: fixed;
    width: 100%;
    text-align: left;
    border-radius: 8px 8px 0 0;
    border-collapse: separate;
    border-spacing: 0;
    margin: 0;
    .table-th {
      padding: 16px;
      color: rgba(0, 0, 0, 0.85);
      font-weight: 500;
      text-align: left;
      background: #fafafa;
      border: none;
      border-bottom: 1px solid #f0f0f0;
      transition: background 0.3s ease;
      &:first-child {
        border-top-left-radius: 8px;
      }
      &:last-child {
        border-top-right-radius: 8px;
      }
    }
    .m-table-body {
      position: relative;
      .table-loading {
        border: none;
        background-color: #fff;
        .loading {
          position: absolute;
          width: 100%;
          height: 100%;
        }
      }
      .table-empty-wrap {
        border: none;
        background-color: #fff;
        &:hover {
          background: #fff;
        }
        .table-empty {
          padding: 16px;
          border: none;
          border-bottom: 1px solid #f0f0f0;
          .empty {
            margin: 32px 0;
          }
        }
      }
    }
    .table-tr {
      border: none;
      background-color: #fff;
      transition: background-color 0.3s;
      .m-td {
        padding: 16px;
        border: none;
        border-bottom: 1px solid #f0f0f0;
        transition: background 0.3s;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
      }
      &:hover {
        background-color: rgba(0, 0, 0, 0.02);
      }
    }
  }
  .mt16 {
    margin-top: 16px;
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import Table from './Table.vue'
import { ref, reactive, onMounted } from 'vue'
const loading = ref(false)
const total = ref(80)
const queryParams = reactive({
        pageSize: 10,
        page: 1
      })
const columns = ref([
        {
          title: '名字',
          width: 100,
          dataIndex: 'name',
          slot: 'name'
        },
        {
          title: '年龄',
          width: 100,
          dataIndex: 'age'
        },
        {
          title: '职业',
          width: 100,
          dataIndex: 'job',
          slot: 'job'
        },
        {
          title: '性别',
          width: 100,
          dataIndex: 'sex'
        },
        {
          title: '地址',
          width: 120,
          dataIndex: 'address'
        }
      ])
const tableData = ref([
        {
          name: 'Stephen',
          age: 30,
          job: 'player',
          sex: '男',
          address: 'CaliforniaCaliforniaCaliforniaCaliforniaCaliforniaCalifornia'
        },
        {
          name: 'Leo',
          age: 36,
          job: 'actor',
          sex: '男',
          address: 'LA'
        },
        {
          name: 'Mr.Dear',
          age: 23,
          job: 'boy',
          sex: '男',
          address: 'Beijing'
        },
        {
          name: 'superman',
          age: 32,
          job: 'boy',
          sex: '男',
          address: 'US'
        }
      ])
onMounted(() => {
  getData()
})
function getData () {
  loading.value = true
  // 模拟调用接口获取列表数据
  setTimeout(() => {
    loading.value = false
  }, 500)
}
function onChange (page: number, pageSize: number) {
  queryParams.page = page
  queryParams.pageSize = pageSize
  getData()
}
</script>
<template>
  <div>
    <h1>{
  { $route.name }} {
  { $route.meta.title }}</h1>
    <h2 class="mt30 mb10">基本使用</h2>
    <Table
      :columns="columns"
      :dataSource="tableData"
      :pagination="{
        page: 1,
        pageSize: 10,
        showQuickJumper: true,
        showTotal: true
      }"
      :total="total"
      :loading="loading"
      @change="onChange">
    <!-- 配置指定列数据 -->
    <template #name="record">
      hello {
  { record.name }}
    </template>
    <template #job="{ job, index }">
      hi {
  { job }}
    </template>
    </Table>
    <h2 class="mt30 mb10">加载中</h2>
    <Table :columns="columns" loading />
    <h2 class="mt30 mb10">暂无数据</h2>
    <Table :columns="columns" :total="0" />
  </div>
</template>
相关文章
|
18天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
21天前
|
API
vue3知识点:provide 与 inject
vue3知识点:provide 与 inject
28 4
vue3知识点:provide 与 inject
|
21天前
|
API
vue3知识点:readonly 与 shallowReadonly
vue3知识点:readonly 与 shallowReadonly
24 1
vue3知识点:readonly 与 shallowReadonly
|
15天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
35 7
|
16天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
38 3
|
15天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
36 1
|
15天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
36 1
|
17天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
|
18天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
21天前
|
JavaScript Java API
vue3知识点:setup
vue3知识点:setup
27 5