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>
相关文章
|
2月前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
163 64
|
1天前
|
资源调度 JavaScript 前端开发
创建vue3项目步骤以及安装第三方插件步骤【保姆级教程】
这是一篇关于创建Vue项目的详细指南,涵盖从环境搭建到项目部署的全过程。
12 1
|
2月前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
143 60
|
27天前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
106 3
|
2月前
|
JavaScript 前端开发 API
从Vue 2到Vue 3的演进
从Vue 2到Vue 3的演进
86 17
|
2月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
101 17
|
2月前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
57 8
|
2月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
52 1
|
2月前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
58 1
|
2月前
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。