Skip to content

Table Component

Table is a high-performance table component built on Uniapp, supporting fixed columns, sorting, striped rows, custom slots, and more. It is suitable for a variety of data display scenarios.

📌 Platform Differences

APP(vue)H5WeChat Mini ProgramAlipay Mini Program

⚠️ Notes

Notes

  1. It is recommended to use numbers for the table height (height) and row height (rowHeight); the component will automatically append the px unit
  2. Fixed columns must specify the width property, otherwise layout issues may occur
  3. For large datasets (>1000 rows), virtual scrolling or paginated loading is recommended to improve performance
  4. The formatter function should be kept as simple as possible; avoid complex calculations that could affect rendering performance
  5. For long text in table content, it is recommended to enable the ellipsis property to avoid layout issues

Key Features

  • Supports left and right fixed columns
  • Header sorting functionality
  • Striped row styles
  • Custom row height and table height
  • Empty state and loading state display
  • Rich slot support
  • Responsive design

🏯 Basic Usage Example

vue
<template>
    <hy-table :data="tableData" :columns="columns" :height="500" stripe></hy-table>
</template>
ts
import { ref } from 'vue';

// Table data
const tableData = ref([
    { id: 1, name: 'Zhang San', age: 28, gender: 'Male', address: 'Chaoyang District, Beijing' },
    { id: 2, name: 'Li Si', age: 32, gender: 'Female', address: 'Pudong New District, Shanghai' },
    { id: 3, name: 'Wang Wu', age: 25, gender: 'Male', address: 'Tianhe District, Guangzhou' },
    // More data...
]);

// Column configuration
const columns = ref([
    { title: 'ID', key: 'id', width: 80, fixed: 'left' },
    { title: 'Name', key: 'name', width: 120 },
    { title: 'Age', key: 'age', width: 100, sortable: true },
    { title: 'Gender', key: 'gender', width: 100 },
    { title: 'Address', key: 'address', width: 300, ellipsis: true },
    { title: 'Actions', key: 'action', width: 150, fixed: 'right' },
]);

Fixed Columns / Slots

Note

Since WeChat Mini Programs cannot use dynamic slots, this slot approach is implemented by passing all content through slots

html
<hy-table :data="tableData" :columns="columns" :height="500">
    <!-- Custom action column -->
    <template #right="{ row, col, index }">
        <hy-flex v-if="col.key === 'action'" gap="2">
            <hy-button text="Edit" size="mini" plain :border="false"></hy-button>
            <hy-button type="error" size="mini" text="Delete" plain :border="false"></hy-button>
        </hy-flex>
        <text v-else>{{ row[col.key] }}</text>
    </template>
</hy-table>
ts
import { reactive } from 'vue';

const columns2 = reactive([
    { title: 'ID', key: 'id', width: 40, fixed: 'left', align: 'center' },
    { title: 'Name', key: 'name', width: 80, fixed: 'left' },
    { title: 'Gender', key: 'sex', width: 80 },
    { title: 'Age', key: 'age', width: 80, sortable: true },
    { title: 'Email', key: 'email', width: 200, ellipsis: true },
    { title: 'Address', key: 'address', width: 300, ellipsis: true },
    { title: 'Phone', key: 'phone', width: 150, ellipsis: true },
    { title: 'Company', key: 'company', width: 150, ellipsis: true },
    { title: 'Position', key: 'position', width: 150, ellipsis: true },
    { title: 'Actions', key: 'action', width: 100, fixed: 'right' },
]);

const tableData = reactive([
    {
        id: 1,
        name: 'Zhang San',
        age: 12,
        sex: 'Male',
        address: 'Feixi County, Hefei City, Anhui Province',
        status: 1,
        email: '[email protected]',
        phone: '13800138001',
        company: 'Tech Co., Ltd.',
        position: 'Frontend Developer',
    },
    {
        id: 2,
        name: 'Li Si',
        age: 18,
        sex: 'Male',
        address: 'Feixi County, Hefei City, Anhui Province',
        status: 1,
        email: '[email protected]',
        phone: '13800138002',
        company: 'Internet Company',
        position: 'Backend Developer',
    },
    {
        id: 3,
        name: 'Wang Er',
        age: 28,
        sex: 'Male',
        address: 'Feixi County, Hefei City, Anhui Province',
        status: 1,
        email: '[email protected]',
        phone: '13800138003',
        company: 'FinTech',
        position: 'Product Manager',
    },
    {
        id: 4,
        name: 'Ma Zi',
        age: 17,
        sex: 'Male',
        address: 'Morin Dawa Daur Autonomous Banner, Hulunbuir, Inner Mongolia',
        status: 0,
        email: '[email protected]',
        phone: '13800138004',
        company: 'E-commerce',
        position: 'Designer',
    },
    {
        id: 5,
        name: 'Sang Biao',
        age: 80,
        sex: 'Male',
        address: 'The Underworld',
        status: 0,
        email: '[email protected]',
        phone: '13800138005',
        company: 'Underworld Tech',
        position: 'CEO',
    },
    {
        id: 6,
        name: 'Aode Biao',
        age: 23,
        sex: 'Male',
        address: 'Africa',
        status: 0,
        email: '[email protected]',
        phone: '13800138006',
        company: 'International Trade',
        position: 'Sales Manager',
    },
    {
        id: 7,
        name: 'Zeus',
        age: 33,
        sex: 'Male',
        address: 'The Solar System, Universe',
        status: 0,
        email: '[email protected]',
        phone: '13800138007',
        company: 'Olympus Group',
        position: 'King of the Gods',
    },
    {
        id: 8,
        name: 'Zhang San',
        age: 12,
        sex: 'Male',
        address: 'Feixi County, Hefei City, Anhui Province',
        status: 1,
        email: '[email protected]',
        phone: '13800138001',
        company: 'Tech Co., Ltd.',
        position: 'Frontend Developer',
    },
    {
        id: 9,
        name: 'Li Si',
        age: 18,
        sex: 'Male',
        address: 'Feixi County, Hefei City, Anhui Province',
        status: 1,
        email: '[email protected]',
        phone: '13800138002',
        company: 'Internet Company',
        position: 'Backend Developer',
    },
    {
        id: 10,
        name: 'Wang Er',
        age: 28,
        sex: 'Male',
        address: 'Feixi County, Hefei City, Anhui Province',
        status: 1,
        email: '[email protected]',
        phone: '13800138003',
        company: 'FinTech',
        position: 'Product Manager',
    },
    {
        id: 11,
        name: 'Ma Zi',
        age: 17,
        sex: 'Male',
        address: 'Morin Dawa Daur Autonomous Banner, Hulunbuir, Inner Mongolia',
        status: 0,
        email: '[email protected]',
        phone: '13800138004',
        company: 'E-commerce',
        position: 'Designer',
    },
    {
        id: 12,
        name: 'Sang Biao',
        age: 80,
        sex: 'Male',
        address: 'The Underworld',
        status: 0,
        email: '[email protected]',
        phone: '13800138005',
        company: 'Underworld Tech',
        position: 'CEO',
    },
    {
        id: 13,
        name: 'Aode Biao',
        age: 23,
        sex: 'Male',
        address: 'Africa',
        status: 0,
        email: '[email protected]',
        phone: '13800138006',
        company: 'International Trade',
        position: 'Sales Manager',
    },
    {
        id: 14,
        name: 'Zeus',
        age: 33,
        sex: 'Male',
        address: 'The Solar System, Universe',
        status: 0,
        email: '[email protected]',
        phone: '13800138007',
        company: 'Olympus Group',
        position: 'King of the Gods',
    },
]);

Sorting

html
<hy-table :data="tableData" :columns="columns" :height="500" @sort-change="handleSort"></hy-table>

<script setup lang="ts">
    import { ref } from 'vue';
    const columns = ref([
        { title: 'Name', key: 'name', width: 120 },
        { title: 'Age', key: 'age', width: 100, sortable: true },
        { title: 'Join Date', key: 'joinDate', width: 150, sortable: true },
    ]);

    const handleSort = (field: string, order: string) => {
        console.log('Sort field:', field, 'Sort order:', order);
        // You can request sorted data from the backend here
    };
</script>

Custom Empty State

html
<hy-table :data="emptyData" :columns="columns" :height="500">
    <template #empty>
        <view class="custom-empty">
            <image
                src="/static/empty.png"
                mode="aspectFit"
                style="width: 120rpx; height: 120rpx;"
            ></image>
            <text>No data yet, click to add</text>
            <button @click="addData">Add Data</button>
        </view>
    </template>
</hy-table>

Loading State

html
<hy-table :data="tableData" :columns="columns" :height="500" :loading="loading"></hy-table>

<script setup lang="ts">
    import { ref, onMounted } from 'vue'

    const loading = ref(true)
    const tableData = ref([])

    onMounted(async () => {
      // Simulate asynchronous loading
      loading.value = true
      await new Promise(resolve => setTimeout(resolve, 2000))
      tableData.value = [...]
      loading.value = false
    })
</script>

Style Customization

Custom Theme

You can customize the table style by overriding CSS variables:

scss
:root {
    --hy-table-header-bg: #f5f7fa;
    --hy-table-header-text-color: #333;
    --hy-table-stripe-bg: #fafafa;
    --hy-table-border-color: #e8e8e8;
}

API

Table Props

ParameterDescriptionTypeDefault
dataTable data sourceArray-
columnsColumn configurationITableColumn[]-
heightTable height, supports a number or a string with a unit; numbers default to pxstring|number400
rowHeightRow height, supports a number or a string with a unit; numbers default to pxstring|number50
stripeWhether to display striped rowsbooleanfalse
borderWhether to display horizontal bordersbooleantrue
loadingWhether to display the loading statebooleanfalse
rowKeyUnique key name for row datastringid
showHeaderWhether to display the headerbooleantrue
emptyUrlImage URL for the empty statestring-
emptyDesDescription text for the empty statestring-

Events

Event NameDescriptionCallback Parameters
sort-changeTriggered when the sort condition changessortField: String, sortOrder: SortType
row-clickTriggered when a row is clickedrow: Object, index: Number

Slots

Header Slots

Slot NameDescriptionParameters
left-headHeader slot for left fixed columnscol: column config object
headHeader slot for middle scrollable columnscol: column config object
right-headHeader slot for right fixed columnscol: column config object

Content Slots

Slot NameDescriptionParameters
leftContent slot for left fixed columnsrow: row data, col: column config, index: row index
defaultContent slot for middle scrollable columnsrow: row data, col: column config, index: row index
rightContent slot for right fixed columnsrow: row data, col: column config, index: row index

Other Slots

Slot NameDescription
emptyCustom empty state

Typings

Type Definitions
ts
export interface ITableColumn {
    /** Column title */
    title: string;
    /** Corresponding data field name */
    key: string;
    /** Column width (numbers default to px) */
    width: number;
    /** Alignment */
    align?: HyApp.RowCenterType;
    /** Whether sorting is enabled */
    sortable?: boolean;
    /** Whether to ellipsize overflowing text */
    ellipsis?: boolean;
    /** Whether clicking shows the full content (Tooltip) when ellipsis is enabled */
    tooltip?: boolean;
    /** Whether fixed ('left' | 'right') */
    fixed?: 'left' | 'right';
    /** Data processing function */
    formatter?: (value: any, row: any) => string;
}

export type SortType = 'asc' | 'desc' | 'normal';
01:10