Vue3表格(Table)

75 篇文章 4 订阅
72 篇文章 3 订阅
本文介绍了如何在Vue2中创建一个自定义表格组件,包括配置表格列(columns)、数据源(dataSource)、分页(pagination)以及加载中和无数据状态。组件支持显示加载中(Spin)、空状态(Empty)和分页器(Pagination)。示例代码展示了组件的结构和使用方法,以及如何处理分页回调事件。
摘要由CSDN通过智能技术生成

Vue2表格(Table)

可自定义设置以下属性:

  • 表格列的配置项(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>
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值