ant design vue 的 表格组件(a-table)自适应父容器大小

4,020 阅读3分钟

为什么写这个

  • 官网没有直接的示例来说明如何自适应父容器大小
  • 网上其他博客上的实现方式过于复杂(需要计算表头高度,计算容器高度,计算其他元素高度,再将容器高度-表头高度-其他元素高度,以此得到最终的表格体高度)

自适应父容器大小是什么意思?

表格占满父容器的宽高,如果父容器的宽高不足以容纳所有数据,则出现滚动条。

实现效果

数据少的情况

image.png

数据多的情况

image.png

存在冻结列的情况

image.png

存在表头分组和冻结列的情况

image.png

已知的适用版本

ant design vue 4.x

实现思路

该方案特点: 无论是否启用分页,都适用

  1. 通过css样式,让表格高度为100%(占满父容器)此时要求父容器必须有高度
  2. 让表格成为一个flex容器,flex的方向为column, 表头设置为flex-shrink: 0,表体设置为flex:1;height:0;
  3. 如果是分页表格,则还需要将表格的父容器设置为flex容器,flex的方向为column,将分页组件所在的dom设置为flex-shrink: 0,再将表格设置为flex:1;height:0;
  4. a-table的x,y都设置为max-content, scroll="{ x: 'max-content', y: 'max-content' }"
  5. 如果max-content设置存在问题,可以换成100%试试

具体代码

<template>
  <!-- vp-raw的作用是隔绝vitepress样式对当前组件的样式影响 -->
  <div class="vp-raw" style="height: 560px">
    <div class="custom-antd-table-wrapper">
      <a-table
        :columns="columns"
        :data-source="data"
        :scroll="{ x: 'max-content', y: 'max-content',hideOnSinglePage:true }"
      >
        <!-- 自定义表头 -->
        <template #headerCell="{ column }">
          <template v-if="column.key === 'name'">
            <span>
              <smile-outlined />
              {{ column.title }}
            </span>
          </template>
        </template>

        <!-- 自定义单元格 -->
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'name'">
            <a>
              {{ record.name }}
            </a>
          </template>
          <template v-else-if="column.key === 'tags'">
            <span>
              <a-tag
                v-for="tag in record.tags"
                :key="tag"
                :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
              >
                {{ tag.toUpperCase() }}
              </a-tag>
            </span>
          </template>
          <template v-else-if="column.key === 'action'">
            <span>
              <a>Invite 一 {{ record.name }}</a>
              <a-divider type="vertical" />
              <a>Delete</a>
              <a-divider type="vertical" />
              <a class="ant-dropdown-link">
                More actions
                <down-outlined />
              </a>
            </span>
          </template>
        </template>
      </a-table>
    </div>
  </div>
</template>
<script lang="ts" setup>
import { ref, onMounted, computed } from 'vue'
import { SmileOutlined, DownOutlined } from '@ant-design/icons-vue'
import type { TableColumnType } from 'ant-design-vue'

interface UserInfo {
  key: string
  name: string
  age: number
  address: string
  tags: string[]
}
const columns: TableColumnType<UserInfo>[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    width: 200,
    key: 'name',
    customCell: () => {
      return {
        style: {
          color: 'red',
        },
        onClick: event => {
          alert(123)
        },
      }
    },
  },
  {
    title: '年龄',
    dataIndex: 'age',
    width: 200,
    key: 'age',
  },
  {
    title: '地址',
    dataIndex: 'address',
    key: 'address',
    width: 100,
    ellipsis: true,
  },
  {
    title: '标签',
    dataIndex: 'tags',
    key: 'tags',
    width: 200,
  },
  {
    title: '动作',
    key: 'action',
    width: 200,
  },
]

const data: UserInfo[] = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
    tags: ['nice', 'developer'],
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
    tags: ['loser'],
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sidney No. 1 Lake Park',
    tags: ['cool', 'teacher'],
  },
]
</script>
<style scoped lang="less">
.custom-antd-table-wrapper {
  height: 100%;
  ::v-deep(.ant-table-wrapper) {
    height: 100%;
    .ant-spin-nested-loading {
      height: 100%;
    }
    .ant-spin-container {
      height: 100%;
      display: flex;
      flex-direction: column;
      .ant-table {
        flex: 1;
        height: 0;
      }
      .ant-pagination {
        flex-shrink: 0;
      }
    }
    .ant-table-container {
      height: 100%;
      display: flex;
      flex-direction: column;
      > .ant-table-header {
        flex-shrink: 0;
      }
      > .ant-table-body {
        flex: 1;
        height: 0;
      }
    }
  }
}
</style>

其他补充问题

  • 自适应父容器大小,如果父容器是模态框之类的弹窗,如果这个弹窗的显示存在动效(比如:从小逐渐增大),那会影响a-table计算父容器的大小。解决方案:要么去除动效,要么等动效执行完毕之后,再渲染a-table