Skip to content

Table 表格

展示行列数据。

何时使用

  • 当有大量结构化的数据需要展现时;
  • 当需要对数据进行排序、搜索、分页、自定义操作等复杂行为时。

如何使用

指定表格的数据源 dataSource 为一个数组。

js
const dataSource = [
  {
    key: '1',
    name: '胡彦斌',
    age: 32,
    address: '西湖区湖底公园1号',
  },
  {
    key: '2',
    name: '胡彦祖',
    age: 42,
    address: '西湖区湖底公园1号',
  },
];

const columns = [
  {
    title: '姓名',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: '年龄',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: '住址',
    dataIndex: 'address',
    key: 'address',
  },
];

基本用法

简单的表格,最后一列是各种操作。

vue
<template>
  <g-table :data-source="dataSource" :columns="columns">
    <template #bodyCell="{ column, record }">
      <template v-if="column.key === 'name'">
        <a>{{ record.name }}</a>
      </template>
      <template v-else-if="column.key === 'tags'">
        <span>
          <g-tag
            v-for="tag in record.tags"
            :key="tag"
            :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
          >
            {{ tag.toUpperCase() }}
          </g-tag>
        </span>
      </template>
      <template v-else-if="column.key === 'action'">
        <span>
          <a>Invite 一 {{ record.name }}</a>
          <g-divider type="vertical" />
          <a>Delete</a>
        </span>
      </template>
    </template>
  </g-table>
</template>

<script>
const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
    key: 'address',
  },
  {
    title: 'Tags',
    key: 'tags',
    dataIndex: 'tags',
  },
  {
    title: 'Action',
    key: 'action',
  },
];

const data = [
  {
    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'],
  },
];

export default {
  setup() {
    return {
      dataSource: data,
      columns,
    };
  },
};
</script>

选择和操作

选择后进行操作,完成后清空选择,通过 rowSelection.selectedRowKeys 来控制选中项。

vue
<template>
  <div>
    <div style="margin-bottom: 16px">
      <g-button type="primary" :disabled="!hasSelected" :loading="loading" @click="start">
        Reload
      </g-button>
      <span style="margin-left: 8px">
        <template v-if="hasSelected">
          {{ `Selected ${selectedRowKeys.length} items` }}
        </template>
      </span>
    </div>
    <g-table :row-selection="rowSelection" :columns="columns" :data-source="data" />
  </div>
</template>

<script>
import { computed, ref } from 'vue';

const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
  },
  {
    title: 'Age',
    dataIndex: 'age',
  },
  {
    title: 'Address',
    dataIndex: 'address',
  },
];

const data = [];
for (let i = 0; i < 46; i++) {
  data.push({
    key: i,
    name: `Edward King ${i}`,
    age: 32,
    address: `London, Park Lane no. ${i}`,
  });
}

export default {
  setup() {
    const selectedRowKeys = ref([]);
    const loading = ref(false);
    
    const hasSelected = computed(() => selectedRowKeys.value.length > 0);
    
    const start = () => {
      loading.value = true;
      // ajax request after empty completing
      setTimeout(() => {
        selectedRowKeys.value = [];
        loading.value = false;
      }, 1000);
    };
    
    const onSelectChange = (newSelectedRowKeys) => {
      console.log('selectedRowKeys changed: ', newSelectedRowKeys);
      selectedRowKeys.value = newSelectedRowKeys;
    };
    
    const rowSelection = {
      selectedRowKeys,
      onChange: onSelectChange,
    };
    
    return {
      data,
      columns,
      rowSelection,
      selectedRowKeys,
      loading,
      hasSelected,
      start,
    };
  },
};
</script>

排序和筛选

对某一列数据进行筛选,使用列的 filters 属性来指定需要筛选菜单的列,onFilter 用于筛选当前数据,filterMultiple 用于指定多选和单选。

对某一列数据进行排序,通过指定列的 sorter 函数即可启动排序功能。sorter: function(a, b) { return a.age - b.age }sorter 也可以是一个 boolean 属性,如果想要服务端排序,可以设置为 true。

vue
<template>
  <g-table :columns="columns" :data-source="data" @change="onChange" />
</template>

<script>
const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    filters: [
      {
        text: 'Joe',
        value: 'Joe',
      },
      {
        text: 'Jim',
        value: 'Jim',
      },
    ],
    // specify the condition of filtering result
    // here is that finding the name started with `value`
    onFilter: (value, record) => record.name.indexOf(value) === 0,
    sorter: (a, b) => a.name.length - b.name.length,
    sortDirections: ['descend'],
  },
  {
    title: 'Age',
    dataIndex: 'age',
    defaultSortOrder: 'descend',
    sorter: (a, b) => a.age - b.age,
  },
  {
    title: 'Address',
    dataIndex: 'address',
    filters: [
      {
        text: 'London',
        value: 'London',
      },
      {
        text: 'New York',
        value: 'New York',
      },
    ],
    filterMultiple: false,
    onFilter: (value, record) => record.address.indexOf(value) === 0,
    sorter: (a, b) => a.address.length - b.address.length,
    sortDirections: ['descend', 'ascend'],
  },
];

const data = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York No. 1 Lake Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 42,
    address: 'London No. 1 Lake Park',
  },
  {
    key: '3',
    name: 'Joe Black',
    age: 32,
    address: 'Sidney No. 1 Lake Park',
  },
  {
    key: '4',
    name: 'Jim Red',
    age: 32,
    address: 'London No. 2 Lake Park',
  },
];

export default {
  setup() {
    const onChange = (pagination, filters, sorter, extra) => {
      console.log('params', pagination, filters, sorter, extra);
    };
    
    return {
      data,
      columns,
      onChange,
    };
  },
};
</script>

可展开

当表格内容较多不能一次性完全展示时。

vue
<template>
  <g-table
    :columns="columns"
    :data-source="data"
    :expand-row-by-click="true"
  >
    <template #expandedRowRender="{ record }">
      <p style="margin: 0">
        {{ record.description }}
      </p>
    </template>
  </g-table>
</template>

<script>
const columns = [
  { title: 'Name', dataIndex: 'name', key: 'name' },
  { title: 'Age', dataIndex: 'age', key: 'age' },
  { title: 'Address', dataIndex: 'address', key: 'address' },
  {
    title: 'Action',
    dataIndex: 'operation',
    key: 'operation',
  },
];

const data = [];
for (let i = 0; i < 3; ++i) {
  data.push({
    key: i,
    name: 'Screem',
    age: 32,
    address: 'New York No. 1 Lake Park',
    description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
  });
}

export default {
  setup() {
    return {
      data,
      columns,
    };
  },
};
</script>

固定列

对于列数很多的数据,可以固定前后的列,横向滚动查看其它数据,需要和 scroll.x 配合使用。

若列头与内容不对齐或出现列重复,请指定固定列的宽度 width。如果指定 width 不生效或出现白色垂直空隙,请尝试建议留一列不设宽度以适应弹性布局,或者检查是否有超长连续字段破坏布局。

vue
<template>
  <g-table :columns="columns" :data-source="data" :scroll="{ x: 1500, y: 300 }" />
</template>

<script>
const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    fixed: 'left',
    width: 100,
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
    fixed: 'left',
    width: 100,
  },
  {
    title: 'Column 1',
    dataIndex: 'address',
    key: '1',
    width: 150,
  },
  {
    title: 'Column 2',
    dataIndex: 'address',
    key: '2',
    width: 150,
  },
  {
    title: 'Column 3',
    dataIndex: 'address',
    key: '3',
    width: 150,
  },
  {
    title: 'Column 4',
    dataIndex: 'address',
    key: '4',
    width: 150,
  },
  {
    title: 'Column 5',
    dataIndex: 'address',
    key: '5',
    width: 150,
  },
  {
    title: 'Column 6',
    dataIndex: 'address',
    key: '6',
    width: 150,
  },
  {
    title: 'Column 7',
    dataIndex: 'address',
    key: '7',
    width: 150,
  },
  { title: 'Column 8', dataIndex: 'address', key: '8' },
  {
    title: 'Action',
    key: 'operation',
    fixed: 'right',
    width: 100,
  },
];

const data = [];
for (let i = 0; i < 100; i++) {
  data.push({
    key: i,
    name: `Edrward ${i}`,
    age: 32,
    address: `London Park no. ${i}`,
  });
}

export default {
  setup() {
    return {
      data,
      columns,
    };
  },
};
</script>

固定头和列

适合同时展示有大量数据和数据列。

若列头与内容不对齐或出现列重复,请指定固定列的宽度 width。 建议指定 scroll.x 为大于表格宽度的固定值或百分比。注意,且非固定列宽度之和不要超过 scroll.x

vue
<template>
  <g-table
    :columns="columns"
    :data-source="data"
    :scroll="{ x: 1500, y: 300 }"
    bordered
  />
</template>

<script>
const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    fixed: 'left',
    width: 100,
  },
  {
    title: 'Age',
    dataIndex: 'age',
    key: 'age',
    fixed: 'left',
    width: 100,
  },
  {
    title: 'Column 1',
    dataIndex: 'address',
    key: '1',
    width: 150,
  },
  {
    title: 'Column 2',
    dataIndex: 'address',
    key: '2',
    width: 150,
  },
  {
    title: 'Column 3',
    dataIndex: 'address',
    key: '3',
    width: 150,
  },
  {
    title: 'Column 4',
    dataIndex: 'address',
    key: '4',
    width: 150,
  },
  {
    title: 'Column 5',
    dataIndex: 'address',
    key: '5',
    width: 150,
  },
  {
    title: 'Column 6',
    dataIndex: 'address',
    key: '6',
    width: 150,
  },
  {
    title: 'Column 7',
    dataIndex: 'address',
    key: '7',
    width: 150,
  },
  { title: 'Column 8', dataIndex: 'address', key: '8' },
  {
    title: 'Action',
    key: 'operation',
    fixed: 'right',
    width: 100,
  },
];

const data = [];
for (let i = 0; i < 100; i++) {
  data.push({
    key: i,
    name: `Edrward ${i}`,
    age: 32,
    address: `London Park no. ${i}`,
  });
}

export default {
  setup() {
    return {
      data,
      columns,
    };
  },
};
</script>

表头分组

columns[n] 可以内嵌 children,以渲染分组表头。

vue
<template>
  <g-table :columns="columns" :data-source="data" bordered />
</template>

<script>
const columns = [
  {
    title: 'Name',
    dataIndex: 'name',
    key: 'name',
    width: 100,
    fixed: 'left',
    filters: [
      {
        text: 'Joe',
        value: 'Joe',
      },
      {
        text: 'John',
        value: 'John',
      },
    ],
    onFilter: (value, record) => record.name.indexOf(value) === 0,
  },
  {
    title: 'Other',
    children: [
      {
        title: 'Age',
        dataIndex: 'age',
        key: 'age',
        width: 150,
        sorter: (a, b) => a.age - b.age,
      },
      {
        title: 'Address',
        children: [
          {
            title: 'Street',
            dataIndex: 'street',
            key: 'street',
            width: 150,
          },
          {
            title: 'Block',
            children: [
              {
                title: 'Building',
                dataIndex: 'building',
                key: 'building',
                width: 100,
              },
              {
                title: 'Door No.',
                dataIndex: 'number',
                key: 'number',
                width: 100,
              },
            ],
          },
        ],
      },
    ],
  },
  {
    title: 'Company',
    children: [
      {
        title: 'Company Address',
        dataIndex: 'companyAddress',
        key: 'companyAddress',
        width: 200,
      },
      {
        title: 'Company Name',
        dataIndex: 'companyName',
        key: 'companyName',
      },
    ],
  },
  {
    title: 'Gender',
    dataIndex: 'gender',
    key: 'gender',
    width: 80,
    fixed: 'right',
  },
];

const data = [];
for (let i = 0; i < 100; i++) {
  data.push({
    key: i,
    name: 'John Brown',
    age: i + 1,
    street: 'Lake Park',
    building: 'C',
    number: 2035,
    companyAddress: 'Lake Street 42',
    companyName: 'SoftLake Co',
    gender: 'M',
  });
}

export default {
  setup() {
    return {
      data,
      columns,
    };
  },
};
</script>

可编辑单元格

带单元格编辑功能的表格。

vue
<template>
  <g-table :data-source="dataSource" :columns="columns" bordered>
    <template #bodyCell="{ column, text, record, index }">
      <template v-if="column.dataIndex === 'name'">
        <div>
          <g-input
            v-if="editableData[record.key]"
            v-model:value="editableData[record.key].name"
            style="margin: -5px 0"
          />
          <template v-else>
            {{ text }}
          </template>
        </div>
      </template>
      <template v-else-if="column.dataIndex === 'operation'">
        <div class="editable-row-operations">
          <span v-if="editableData[record.key]">
            <g-typography-link @click="save(record.key)">Save</g-typography-link>
            <g-popconfirm title="Sure to cancel?" @confirm="cancel(record.key)">
              <a>Cancel</a>
            </g-popconfirm>
          </span>
          <span v-else>
            <a @click="edit(record.key)">Edit</a>
          </span>
        </div>
      </template>
    </template>
  </g-table>
</template>

<script>
import { cloneDeep } from 'lodash-es';
import { reactive, ref } from 'vue';

const columns = [
  {
    title: 'name',
    dataIndex: 'name',
    width: '25%',
  },
  {
    title: 'age',
    dataIndex: 'age',
    width: '15%',
  },
  {
    title: 'address',
    dataIndex: 'address',
    width: '40%',
  },
  {
    title: 'operation',
    dataIndex: 'operation',
  },
];

const data = [];
for (let i = 0; i < 100; i++) {
  data.push({
    key: i.toString(),
    name: `Edrward ${i}`,
    age: 32,
    address: `London Park no. ${i}`,
  });
}

export default {
  setup() {
    const dataSource = ref(data);
    const editableData = reactive({});
    
    const edit = (key) => {
      editableData[key] = cloneDeep(dataSource.value.filter(item => key === item.key)[0]);
    };
    
    const save = (key) => {
      Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
      delete editableData[key];
    };
    
    const cancel = (key) => {
      delete editableData[key];
    };
    
    return {
      dataSource,
      columns,
      editableData,
      edit,
      save,
      cancel,
    };
  },
};
</script>

<style>
.editable-row-operations a {
  margin-right: 8px;
}
</style>

API

Table

参数说明类型默认值版本
bordered是否展示外边框和列边框booleanfalse
childrenColumnName指定树形结构的列名stringchildren
columns表格列的配置描述,具体项见下表array-
components覆盖默认的 table 元素object-
dataSource数据数组any[]-
defaultExpandAllRows初始时,是否展开所有行booleanfalse
defaultExpandedRowKeys默认展开的行string[]-
expandedRowKeys展开的行,控制属性string[]-
expandedRowRender额外的展开行Function(record, index, indent, expanded):VNode | v-slot:expandedRowRender="{record, index, indent, expanded}"-
expandIcon自定义展开图标,参考示例Function(props):VNode | v-slot:expandIcon="props"-
expandRowByClick通过点击行来展开子行booleanfalse
footer表格尾部Function(currentPageData) | v-slot:footer="currentPageData"-
getPopupContainer设置表格内各类浮层的渲染节点,如筛选菜单(triggerNode) => HTMLElement() => TableHtmlElement
indentSize展示树形数据时,每层缩进的宽度,以 px 为单位number15
loading页面是否加载中boolean| objectfalse
locale默认文案设置,目前包括排序、过滤、空数据文案objectfilterConfirm: '确定'
filterReset: '重置'
emptyText: '暂无数据'
pagination分页器,参考配置项或 pagination 文档,设为 false 时不展示和进行分页object | false-
rowClassName表格行的类名Function(record, index):string-
rowKey表格行 key 的取值,可以是字符串或一个函数string|Function(record):string'key'
rowSelection表格行是否可选择,配置项object-
scroll表格是否可滚动,也可以指定滚动区域的宽、高,配置项object-
showHeader是否显示表头booleantrue
showSorterTooltip表头是否显示下次排序的 tooltip 提示。当参数类型为对象时,将被设置为 Tooltip 的属性boolean | Tooltip propstrue
size表格大小default | middle | smalldefault
sortDirections支持的排序方式,取值为 ascend descendArray[ascend, descend]
sticky设置粘性头部和滚动条boolean | {offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}-4.0.0
summary总结栏v-slot:summary-
tableLayout表格元素的 table-layout 属性,设为 fixed 表示内容不会影响列的布局- | 'auto' | 'fixed'
固定表头/列或使用了 column.ellipsis 时,默认值为 fixed
title表格标题Function(currentPageData) | v-slot:title="currentPageData"-
transformCellText数据渲染前可以再次改变,一般用户空数据的默认配置Function({ text, column, record, index }) => any-1.5.4

事件

事件名称说明回调参数
change分页、排序、筛选变化时触发Function(pagination, filters, sorter, { currentDataSource })
expand点击展开图标时触发Function(expanded, record)
expandedRowsChange展开的行变化时触发Function(expandedRows)
resizeColumn拖拽列时触发Function(width, column)

Column

列描述数据对象,是 columns 中的一项,Column 使用相同的 API。

参数说明类型默认值版本
align设置列的对齐方式left | right | centerleft
colSpan表头列合并,设置为 0 时,不渲染number-
dataIndex列数据在数据项中对应的路径,支持通过数组查询嵌套路径string | string[]-
defaultFilteredValue默认筛选值string[]-
defaultSortOrder默认排序顺序ascend | descend-
ellipsis超过宽度将自动省略,暂不支持和排序筛选一起使用。
设置为 true{ showTitle?: boolean } 时,表格布局将变成 tableLayout="fixed"
boolean |false
filterDropdown可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互VNode | (props: FilterDropdownProps) => VNode-
filterDropdownVisible用于控制自定义筛选菜单是否可见boolean-
filtered标识数据是否经过过滤,筛选图标会高亮booleanfalse
filteredValue筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组string[]-
filterIcon自定义 filter 图标。VNode | ({filtered: boolean, column: Column}) => vNodefalse
filterMultiple是否多选booleantrue
filters表头的筛选菜单项object[]-
fixed(IE 下无效)列是否固定,可选 true (等效于 left) left rightboolean | stringfalse
keyVue 需要的 key,如果已经设置了唯一的 dataIndex,可以忽略这个属性string-
maxWidth拖拽列最大宽度,会受到表格自动调整分配宽度影响number-1.5.0
minWidth拖拽列最小宽度,会受到表格自动调整分配宽度影响number501.5.0
resizable是否可拖拽调整宽度,此时 width 必须是 number 类型boolean-1.5.0
responsive响应式 breakpoint 配置列表。未设置则始终可见。Breakpoint[]-1.5.0
rowScope设置列范围row | rowgroup-4.1.0
showSorterTooltip表头显示下次排序的 tooltip 提示, 覆盖 table 中 showSorterTooltipboolean | Tooltip propstrue
sortDirections支持的排序方式,覆盖 TablesortDirections, 取值为 ascend descendArray[ascend, descend]
sorter排序函数,本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 trueFunction | boolean-
sortOrder排序的受控属性,外界可用此控制列的排序,可设置为 ascend descend falseboolean | string-
title列头显示文字(函数用法 3.0.0 后支持)string | VNode | (props: { filters: TableColumnFilter[], sortOrder: SortOrder, sortColumn: TableColumn }) => VNode-
width列宽度(指定了也不生效?string | number-
customCell设置单元格属性Function(record, rowIndex)-
customHeaderCell设置头部单元格属性Function(column)-
onFilter本地模式下,确定筛选的运行函数Function-
onFilterDropdownVisibleChange自定义筛选菜单可见性改变时调用function(visible) {}-
slots使用 columns 时,可以通过该属性配置支持 slot 的属性,如 slots: { title: 'XXX'}object-

rowSelection

选择功能的配置。

参数说明类型默认值版本
checkStrictlycheckable 状态下节点选择完全受控(父子节点选中状态不再关联)booleantrue4.0.0
columnWidth自定义列表选择框宽度string | number32px
columnTitle自定义列表选择框标题string | VNode-
fixed把选择框列固定在左边boolean-
getCheckboxProps选择框的默认属性配置Function(record)-
hideSelectAll隐藏全选勾选框与自定义选择项booleanfalse4.3.0
preserveSelectedRowKeys当数据被删除时仍然保留选项的 keyboolean-4.0.0
renderCell渲染勾选框,用法与 Column 的 customCell 相同Function(checked, record, index, originNode)-4.1.0
selectedRowKeys指定选中项的 key 数组,需要和 onChange 进行配合string[] | number[][]
selections自定义选择项 配置项, 设为 true 时使用默认选择项object[] | booleantrue
type多选/单选,checkbox or radiostringcheckbox
onChange选中项发生变化时的回调Function(selectedRowKeys, selectedRows)-
onSelect用户手动选择/取消选择某行的回调Function(record, selected, selectedRows, nativeEvent)-
onSelectAll用户手动选择/取消选择所有行的回调Function(selected, selectedRows, changeRows)-
onSelectInvert用户手动选择反选的回调Function(selectedRowKeys)-
onSelectNone用户清空选择的回调Function()-4.0.0

scroll

参数说明类型默认值
scrollToFirstRowOnChange当分页、排序、筛选变化后是否滚动到表格顶部boolean-
x设置横向滚动,也可用于指定滚动区域的宽,可以设置为像素值,百分比,true 和 'max-content'string | number | true-
y设置纵向滚动,也可用于指定滚动区域的高,可以设置为像素值string | number-

selection

参数说明类型默认值
keyVue 需要的 key,建议设置string-
text选择项显示的文字string | VNode-
onSelect选择项点击回调Function(changeableRowKeys)-

注意

按照 Vue 的规范,所有的数组必须有 key 属性,否则 table 的某些功能可能会出现问题。

js
// 比如你的数据主键是 uid
return <Table rowKey="uid" />;
// 或
return <Table rowKey={record => record.uid} />;

Released under the MIT License.