Skip to content

Table 表格

组件注册

js
import FTable from '@fesjs/fes-design';

app.use(FTable);

代码演示

基础用法

FTable 元素中注入 data 对象数组后,在 FTableColumn 中用 prop 属性来对应对象中的键名即可填入数据,用 label 属性来定义表格的列名。可已使用width 属性来定义列宽。

play
<template>
    <FForm labelWidth="150px">
        <FFormItem label="列宽度设置方式:">
            <FRadioGroup
                v-model="layout"
                :options="[
                    { label: '等分(默认)', value: 'fixed' },
                    { label: '按内容自动调整大小比例', value: 'auto' },
                ]"
            />
        </FFormItem>
        <FFormItem label="是否开启 hover 行样式:">
            <FRadioGroup
                v-model="hoverable"
                :options="[
                    { label: '是(默认)', value: true },
                    { label: '', value: false },
                ]"
            />
        </FFormItem>
        <FFormItem label="是否开启斑马线条纹:">
            <FRadioGroup
                v-model="striped"
                :options="[
                    { label: '', value: true },
                    { label: '否(默认)', value: false },
                ]"
            />
        </FFormItem>
    </FForm>

    <FDivider />

    <FTable
        :data="data"
        :layout="layout"
        :hoverable="hoverable"
        :striped="striped"
    >
        <FTableColumn prop="date" label="日期">
            <template #default="{ row }">
                {{ row.date }}
            </template>
        </FTableColumn>
        <FTableColumn prop="name" label="姓名" />
        <FTableColumn prop="address" label="地址" />
    </FTable>
</template>

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

const data = reactive(
    Array.from([1, 2, 3], (i) => {
        return {
            date: `2016-05-${i < 10 ? `0${i}` : i}`,
            name: '王小虎',
            address: '上海市普陀区金沙江路 1516 弄',
        };
    }),
);
const layout = ref('fixed');
const hoverable = ref(true);
const striped = ref(true);
</script>

边框和分割线

默认有水平分割线,配置horizontalLine=false则无水平分割线。

默认无垂直分割线,配置verticalLine=true则有垂直分割线。

play
<template>
    <FForm>
        <FFormItem label="是否展示表格边框:">
            <FRadioGroup
                v-model="bordered"
                :options="[
                    { label: '否(默认)', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem label="是否展示水平分割线:">
            <FRadioGroup
                v-model="horizontalLine"
                :options="[
                    { label: '', value: false },
                    { label: '是(默认)', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem label="是否展示垂直分割线:">
            <FRadioGroup
                v-model="verticalLine"
                :options="[
                    { label: '否(默认)', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
    </FForm>

    <FDivider />

    <FTable
        :bordered="bordered"
        :horizontalLine="horizontalLine"
        :verticalLine="verticalLine"
        :data="data"
    >
        <FTableColumn prop="date" label="日期" />
        <FTableColumn prop="name" label="姓名" />
        <FTableColumn prop="address" label="地址" />
    </FTable>
</template>

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

export default defineComponent({
    setup() {
        const bordered = ref(false);
        const horizontalLine = ref(true);
        const verticalLine = ref(false);

        const data = reactive(
            Array.from([1, 2, 3], (i) => {
                return {
                    date: `2016-05-${i < 10 ? `0${i}` : i}`,
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1516 弄',
                };
            }),
        );
        return {
            bordered,
            horizontalLine,
            verticalLine,
            data,
        };
    },
});
</script>

固定表头

纵向内容过多时,可选择固定表头。配置height属性,当内容高度超出时出现滚动条。

play
<template>
    <FForm labelWidth="100px">
        <FFormItem label="是否固定表头:">
            <FRadioGroup
                v-model="fixedHeader"
                :options="[
                    { label: '', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem v-if="fixedHeader" label="固定高度:">
            <FInputNumber
                v-model="height"
                :min="10"
                :max="1000"
                :step="50"
            />
            <span style="margin-left: 10px">px</span>
        </FFormItem>
        <FFormItem label="数据行数:">
            <FInputNumber
                v-model="rowLength"
                :min="0"
                :max="100"
            />
        </FFormItem>
        <FFormItem label="姓名列描述:">
            <FInput v-model="nameLabel" :maxlength="30" showWordLimit />
        </FFormItem>
    </FForm>

    <FDivider />

    <FTable
        rowKey="id"
        :data="data"
        bordered
        :height="fixedHeader ? height : undefined"
    >
        <FTableColumn type="selection" :width="30" />
        <FTableColumn
            prop="date"
            label="日期"
            ellipsis
            :width="150"
        />
        <FTableColumn
            prop="name"
            :label="nameLabel || '姓名'"
            :width="150"
        />
        <FTableColumn prop="province" label="省份" :width="150" />
        <FTableColumn prop="city" label="市区" :width="150" />
        <FTableColumn prop="address" label="地址" :width="800" />
        <FTableColumn prop="zip" label="邮编" :width="120" />
        <FTableColumn
            label="操作"
            align="center"
            :width="200"
            :action="action"
        />
    </FTable>
</template>

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

export default {
    setup() {
        const fixedHeader = ref(true);
        const height = ref(250);
        const nameLabel = ref('自定义列描述');

        const rowLength = ref(5);

        const data = computed(() => {
            return Array.from({ length: rowLength.value }, (_, i) => {
                return {
                    id: i,
                    date: `2016-05-2016-05-2016-05-2016-05-${
                        i < 10 ? `0${i}` : i
                    }`,
                    name: '王小虎',
                    province: '上海',
                    city: '普陀区',
                    address: '上海市普陀区金沙江路 1518 弄',
                    zip: 200333,
                };
            });
        });

        const action = [
            {
                label: '编辑',
                func: (row) => {
                    console.log('[table.scroll] [action.编辑] row:', row);
                },
            },
            {
                label: '删除',
                func: (row) => {
                    console.log('[table.scroll] [action.删除] row:', row);
                },
            },
        ];
        return {
            fixedHeader,
            height,
            nameLabel,
            data,
            action,
            rowLength,
        };
    },
};
</script>

固定列

横向内容过多时,可选择固定列。

play
<template>
    <FForm labelWidth="150px">
        <FFormItem label="左边列是否固定:">
            <FRadioGroup
                v-model="fixedLeft"
                :options="[
                    { label: '', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem label="右边列是否固定:">
            <FRadioGroup
                v-model="fixedRight"
                :options="[
                    { label: '', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
    </FForm>

    <FDivider />

    <FTable rowKey="id" :data="data" bordered>
        <FTableColumn
            type="selection"
            :width="30"
            :fixed="fixedLeft ? 'left' : undefined"
        />
        <FTableColumn
            prop="date"
            label="日期"
            ellipsis
            :width="150"
            :fixed="fixedLeft ? true : undefined"
        />
        <FTableColumn prop="name" label="姓名" :width="150" />
        <FTableColumn prop="province" label="省份" :width="150" />
        <FTableColumn prop="city" label="市区" :width="150" />
        <FTableColumn prop="address" label="地址" :width="800" />
        <FTableColumn
            prop="zip"
            label="邮编"
            :width="120"
            :fixed="fixedRight ? 'right' : undefined"
        />
        <FTableColumn
            label="操作"
            align="center"
            :width="200"
            :action="action"
            :fixed="fixedRight ? 'right' : undefined"
        />
    </FTable>
</template>

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

export default {
    setup() {
        const fixedLeft = ref(true);
        const fixedRight = ref(true);

        const data = Array.from([1, 2, 3], (i) => {
            return {
                id: i,
                date: `2016-05-2016-05-2016-05-2016-05-${i < 10 ? `0${i}` : i}`,
                name: '王小虎',
                province: '上海',
                city: '普陀区',
                address: '上海市普陀区金沙江路 1518 弄',
                zip: 200333,
            };
        });
        const action = [
            {
                label: '编辑',
                func: (row) => {
                    console.log('[table.scroll] [action.编辑] row:', row);
                },
            },
            {
                label: '删除',
                func: (row) => {
                    console.log('[table.scroll] [action.删除] row:', row);
                },
            },
        ];
        return {
            fixedLeft,
            fixedRight,
            data,
            action,
        };
    },
};
</script>

固定列和表头

横纵内容过多时,可选择固定列和表头。

play
<template>
    <FTable rowKey="id" :data="data" bordered :height="250">
        <FTableColumn type="selection" :width="30" fixed="left" />
        <FTableColumn
            prop="date"
            label="日期"
            ellipsis
            :width="150"
            :fixed="true"
        />
        <FTableColumn
            prop="name"
            label="姓名姓名姓名姓名姓名姓名姓名姓名"
            :width="150"
        />
        <FTableColumn prop="province" label="省份" :width="150" />
        <FTableColumn prop="city" label="市区" :width="150" />
        <FTableColumn prop="address" label="地址" :width="800" />
        <FTableColumn prop="zip" label="邮编" :width="120" fixed="right" />
        <FTableColumn
            label="操作"
            align="center"
            :width="200"
            :action="action"
            fixed="right"
        />
    </FTable>
</template>

<script>
import {} from 'vue';

export default {
    setup() {
        const data = Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (i) => {
            return {
                id: i,
                date: `2016-05-2016-05-2016-05-2016-05-${i < 10 ? `0${i}` : i}`,
                name: '王小虎',
                province: '上海',
                city: '普陀区',
                address: '上海市普陀区金沙江路 1518 弄',
                zip: 200333,
            };
        });
        const action = [
            {
                label: '编辑',
                func: (row) => {
                    console.log('[table.scroll] [action.编辑] row:', row);
                },
            },
            {
                label: '删除',
                func: (row) => {
                    console.log('[table.scroll] [action.删除] row:', row);
                },
            },
        ];
        return {
            data,
            action,
        };
    },
};
</script>

多级表头

数据结构比较复杂的时候,可使用多级表头来展现数据的层次关系。

play
<template>
    <FForm labelWidth="100px">
        <FFormItem label="是否固定表头:">
            <FRadioGroup
                v-model="fixedHeader"
                :options="[
                    { label: '', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem label="是否固定列:">
            <FRadioGroup
                v-model="fixedColumn"
                :options="[
                    { label: '', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
    </FForm>

    <FDivider />

    <FTable
        :data="data"
        bordered
        verticalLine
        :height="fixedHeader ? 350 : undefined"
    >
        <FTableColumn
            prop="date"
            label="日期"
            :ellipsis="{ tooltip: { popperClass: 'a', showAfter: 500 } }"
            :width="150"
            :fixed="fixedColumn ? 'left' : false"
        />
        <FTableColumn
            prop="name"
            label="姓名"
            :width="150"
            :fixed="fixedColumn ? 'left' : false"
        />
        <FTableColumn label="配送信息">
            <FTableColumn prop="name" label="姓名" :width="150" />
            <FTableColumn label="地址信息">
                <FTableColumn
                    prop="province"
                    label="省份"
                    :width="150"
                />
                <FTableColumn prop="city" label="市区" :width="150" />
                <FTableColumn
                    prop="address"
                    label="详细地址"
                    :width="500"
                />
                <FTableColumn prop="zip" label="邮编" :width="120" />
            </FTableColumn>
        </FTableColumn>
        <FTableColumn
            prop="zip"
            label="邮编"
            :width="120"
            :fixed="fixedColumn ? 'right' : false"
        />
        <FTableColumn
            label="操作"
            align="center"
            :width="200"
            :action="action"
            :fixed="fixedColumn ? 'right' : false"
        />
    </FTable>
</template>

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

export default defineComponent({
    setup() {
        const data = Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (i) => {
            return {
                date: `2016-05-2016-05-2016-05-2016-05-2016-05-2016-05-2016-05-2016-05-${
                    i < 10 ? `0${i}` : i
                }`,
                name: '王小虎',
                province: '上海',
                city: '普陀区',
                address: '上海市普陀区金沙江路 1518 弄',
                zip: 200333,
            };
        });

        const fixedHeader = ref(true);
        const fixedColumn = ref(false);

        const action = [
            {
                label: '编辑',
                func: (row) => {
                    console.log('[table.scroll] [action.编辑] row:', row);
                },
            },
            {
                label: '删除',
                func: (row) => {
                    console.log('[table.scroll] [action.删除] row:', row);
                },
            },
        ];

        return {
            data,
            fixedHeader,
            fixedColumn,
            action,
        };
    },
});
</script>

行选择

选择行数据时使用 Checkbox,type为selection生效。 在该模式下,可以设置multiple,设置单选和多选。

play
<template>
    <FSpace>
        是否多选:
        <FRadioGroup
            v-model="multiple"
            :options="[
                { label: '是(默认)', value: true },
                { label: '', value: false },
            ]"
        />
    </FSpace>
    <div style="margin-bottom: 10px">选中的keys: {{ checkedKeys }}</div>
    <FTable
        ref="multipleTable"
        v-model:checkedKeys="checkedKeys"
        :data="data"
        rowKey="id"
        @selectionChange="selectionChange"
    >
        <FTableColumn
            type="selection"
            :selectable="selectable"
            :multiple="multiple"
        />
        <FTableColumn prop="date" label="日期" />
        <FTableColumn prop="name" label="姓名" />
        <FTableColumn prop="address" label="地址" />
    </FTable>
    <div style="margin-top: 10px">
        <FButton @click="toggleSelection(data[0])">切换第一行</FButton>
        <FButton @click="toggleSelection(null)">取消选择</FButton>
    </div>
</template>

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

export default {
    setup() {
        const checkedKeys = ref([1]);

        const multiple = ref(true);
        const data = reactive(
            Array.from([1, 2, 3], (i) => {
                return {
                    id: i,
                    date: `2016-05-${i < 10 ? `0${i}` : i}`,
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1516 弄',
                };
            }),
        );
        const selectable = ({ rowIndex }) => {
            return rowIndex !== 1;
        };
        const selectionChange = (selection) => {
            console.log(
                '[table.checkbox] [selectionChange] selection:',
                selection,
            );
        };
        const multipleTable = ref(null);
        const toggleSelection = (row) => {
            if (row) {
                multipleTable.value.toggleRowSelection({ row });
            } else {
                multipleTable.value.clearSelection();
            }
        };

        return {
            checkedKeys,
            data,
            selectable,
            multiple,
            multipleTable,
            toggleSelection,
            selectionChange,
        };
    },
};
</script>

排序

play
<template>
    <f-space vertical>
        <f-space>
            <FButton @click="toggleSort">Sort By ID(ascend)</FButton>
            <FButton @click="clearSorter">Clear Sorter</FButton>
        </f-space>
        <FTable ref="table" :data="data" layout="auto">
            <FTableColumn sortable prop="id" label="ID" />
            <FTableColumn sortable prop="date" label="日期">
                <template #default="{ row }">
                    {{ row.date }}
                </template>
            </FTableColumn>
            <FTableColumn prop="name" label="姓名" />
            <FTableColumn prop="address" label="地址" />
        </FTable>
    </f-space>
</template>

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

export default {
    setup() {
        const table = ref();
        const data = reactive(
            Array.from([1, 2, 3], (i) => {
                return {
                    id: 4 - i,
                    date: `2016-05-${i < 10 ? `0${i}` : i}`,
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1516 弄',
                };
            }),
        );
        const toggleSort = () => {
            table.value.sort('id', 'ascend');
        };
        const clearSorter = () => {
            table.value.clearSorter();
        };
        return {
            table,
            data,
            toggleSort,
            clearSorter,
        };
    },
};
</script>

操作

简单的操作类型。

play
<template>
    <FTable :data="data">
        <FTableColumn prop="date" label="日期" />
        <FTableColumn prop="name" label="姓名" />
        <FTableColumn prop="address" label="地址" />
        <FTableColumn
            label="操作"
            align="center"
            :width="200"
            :action="action"
        />
    </FTable>
</template>

<script>
import { reactive } from 'vue';

export default {
    setup() {
        const data = reactive(
            Array.from([1, 2, 3], (i) => {
                return {
                    date: `2016-05-${i < 10 ? `0${i}` : i}`,
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1516 弄',
                };
            }),
        );
        const action = [
            {
                label: '编辑',
                func: (row) => {
                    console.log('[table.action] [action.编辑] row:', row);
                },
            },
            {
                label: '删除',
                func: (row) => {
                    console.log('[table.action] [action.删除] row:', row);
                },
            },
        ];
        return {
            data,
            action,
        };
    },
};
</script>

自定义列内容

自定义列的显示内容,可组合其他组件使用。

play
<template>
    <FTable :data="data">
        <FTableColumn v-slot="{ row }" prop="date" label="日期" :width="150">
            <div class="table-custom-content-date">
                <ClockCircleOutlined /><FEllipsis
                    :content="row.date"
                />
            </div>
        </FTableColumn>
        <FTableColumn v-slot="{ row }" prop="name" label="姓名">
            <FTag>{{ row.name }}</FTag>
        </FTableColumn>
        <FTableColumn prop="address" label="地址" />
        <FTableColumn v-slot="{ row }" label="操作">
            <FButton @click="() => handleClickRow(row)">编辑</FButton>
        </FTableColumn>
    </FTable>

    <FDivider />

    <FTable :data="data" :columns="columns" />
</template>

<script>
import { defineComponent, h, reactive } from 'vue';
import { ClockCircleOutlined } from '@fesjs/fes-design/icon';
import { FButton, FEllipsis, FTag } from '@fesjs/fes-design';

export default defineComponent({
    comments: {
        ClockCircleOutlined,
    },
    setup() {
        const handleClickRow = (row) => {
            console.log('[table.customContent] [handleClickRow] row:', row);
        };

        const data = reactive(
            Array.from([1, 2], (i) => {
                return {
                    date: `2016-05-2016-05-2016-05-2016-05-${
                        i < 10 ? `0${i}` : i
                    }`,
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1516 弄',
                };
            }),
        );
        const columns = [
            {
                prop: 'date',
                label: '日期',
                width: 150,
                ellipsis: true,
                render: ({ row }) => {
                    return h('div', { class: 'table-custom-content-date' }, [
                        h(ClockCircleOutlined),
                        h(FEllipsis, {
                            content: row.date,
                        }),
                    ]);
                },
            },
            {
                prop: 'name',
                label: '姓名1',
                render: ({ row }) => {
                    return h(FTag, {}, () => row.name);
                },
            },
            {
                prop: 'name',
                label: '姓名2',
                formatter: ({ row }) => {
                    return h(FTag, {}, () => row.name);
                },
            },
            {
                prop: 'address',
                label: '地址',
                formatter: ({ row }) => {
                    return row.address;
                },
            },
            {
                prop: 'action',
                label: '操作',
                render: ({ row }) => {
                    return h(
                        FButton,
                        { onClick: () => handleClickRow(row) },
                        () => '编辑',
                    );
                },
            },
        ];

        return {
            data,
            columns,
            handleClickRow,
        };
    },
});
</script>

<style>
.table-custom-content-date {
    display: flex;
    align-items: center;
}
</style>

自定义表头

play
<template>
    <FTable :data="data">
        <FTableColumn prop="date" label="日期" ellipsis :width="150">
            <template #header>
                <div class="table-custom-header-date">
                    <ClockCircleOutlined /> <span>日期</span>
                </div>
            </template>
        </FTableColumn>
        <FTableColumn prop="name" label="姓名" :width="150" />
        <FTableColumn prop="province" label="省份" :width="150" />
        <FTableColumn prop="city" label="市区" :width="150" />
        <FTableColumn prop="address" label="地址" :width="800" />
        <FTableColumn prop="zip" label="邮编" :width="120" />
    </FTable>

    <FDivider />

    <FTable :data="data" :columns="columns" />
</template>

<script>
import { defineComponent, h } from 'vue';
import { ClockCircleOutlined } from '@fesjs/fes-design/icon';

export default defineComponent({
    comments: {
        ClockCircleOutlined,
    },
    setup() {
        const data = Array.from([1, 2], (i) => {
            return {
                date: `2016-05-2016-05-2016-05-2016-05-${i < 10 ? `0${i}` : i}`,
                name: '王小虎',
                province: '上海',
                city: '普陀区',
                address: '上海市普陀区金沙江路 1518 弄',
                zip: 200333,
            };
        });

        const columns = [
            {
                prop: 'date',
                label: '日期',
                width: 150,
                ellipsis: true,
                renderHeader: () => {
                    return h('div', { class: 'table-custom-header-date' }, [
                        h(ClockCircleOutlined),
                        h('span', '日期'),
                    ]);
                },
            },
            {
                prop: 'name',
                label: '姓名',
                width: 150,
            },
            {
                prop: 'province',
                label: '省份',
                width: 150,
            },
            {
                prop: 'city',
                label: '市区',
                width: 150,
            },
            {
                prop: 'address',
                label: '地址',
                width: 800,
            },
            {
                prop: 'zip',
                label: '邮编',
                width: 120,
            },
        ];
        return {
            data,
            columns,
        };
    },
});
</script>

<style>
.table-custom-header-date {
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>

虚拟滚动

1W 行数据也不会卡~

play
<template>
    <FForm labelWidth="120px">
        <FFormItem label="是否虚拟滚动:">
            <FRadioGroup
                v-model="virtualScroll"
                :options="[
                    { label: '', value: false },
                    { label: '', value: true },
                ]"
                @change="() => virtualScroll && (isFixedHeight = true)"
            />
        </FFormItem>
        <FFormItem label="是否指定高度:">
            <FRadioGroup
                v-model="isFixedHeight"
                :disabled="virtualScroll"
                :options="[
                    { label: '', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem v-if="isFixedHeight" label="固定高度:">
            <FInputNumber
                v-model="height"
                :min="50"
                :max="1000"
                :step="50"
            />
            <span style="margin-left: 10px">px</span>
        </FFormItem>
        <FFormItem label="总是显示滚动条:">
            <FRadioGroup
                v-model="alwaysScrollbar"
                :options="[
                    { label: '否(默认)', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
    </FForm>

    <FDivider />

    <FTable
        :virtualScroll="virtualScroll"
        :height="isFixedHeight ? height : undefined"
        rowKey="date"
        :data="data"
        :alwaysScrollbar="alwaysScrollbar"
    >
        <FTableColumn prop="date" label="日期" :width="150" ellipsis fixed />
        <FTableColumn prop="name" label="姓名" :width="150" />
        <FTableColumn prop="province" label="省份" :width="150" />
        <FTableColumn prop="city" label="市区" :width="150" />
        <FTableColumn prop="address" label="地址" :width="800" />
        <FTableColumn prop="zip" label="邮编" :width="120" />
        <FTableColumn
            label="操作"
            align="center"
            :width="200"
            :action="action"
            fixed="right"
        />
    </FTable>
</template>

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

const createData = (n) => {
    const arr = [];
    for (let i = 0; i < n; i++) {
        arr.push({
            date: `2016-05-${i < 10 ? `0${i}` : i}`,
            name: '王小虎',
            province: '上海',
            city: '普陀区',
            address: '上海市普陀区金沙江路 1518 弄',
            zip: 200333,
        });
    }
    return arr;
};

export default {
    setup() {
        const virtualScroll = ref(true);
        const isFixedHeight = ref(true);
        const height = ref(250);
        const alwaysScrollbar = ref(false);

        const data = reactive(createData(200));
        const action = [
            {
                label: '编辑',
                func: (row) => {
                    console.log('[table.virtual] [action.编辑] row:', row);
                },
            },
            {
                label: '删除',
                func: (row) => {
                    console.log('[table.virtual] [action.删除] row:', row);
                },
            },
        ];
        return {
            data,
            action,
            virtualScroll,
            isFixedHeight,
            height,
            alwaysScrollbar,
        };
    },
};
</script>

可拖拽

play
<template>
    <f-space vertical>
        <FSwitch v-model="draggable">
            <template #active></template>
            <template #inactive></template>
        </FSwitch>
        <FTable
            :data="data"
            :draggable="draggable"
            layout="auto"
            @dragstart="onDragstart"
            @dragend="onDragend"
        >
            <FTableColumn prop="date" label="日期">
                <template #default="{ row }">
                    {{ row.date }}
                </template>
            </FTableColumn>
            <FTableColumn prop="name" label="姓名" />
            <FTableColumn prop="address" label="地址" />
        </FTable>
    </f-space>
</template>

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

export default {
    setup() {
        const data = ref(
            Array.from([1, 2, 3], (i) => {
                return {
                    date: `2016-05-${i < 10 ? `0${i}` : i}`,
                    name: '王小虎',
                    address: '上海市普陀区金沙江路 1516 弄',
                };
            }),
        );
        const onDragstart = (...arg) => {
            console.log('[table.draggable] [onDragstart] arg:', arg);
        };
        const onDragend = (...arg) => {
            console.log('[table.draggable] [onDragend] arg:', arg);
        };
        const draggable = ref(true);
        return {
            data,
            onDragstart,
            onDragend,
            draggable,
        };
    },
};
</script>

展开行

当行内容过多并且不想显示横向滚动条时,可以使用展开行功能。

play
<template>
    <div style="margin-bottom: 10px">展开的行keys: {{ expandedKeys }}</div>
    <FTable
        ref="tableRef"
        v-model:expandedKeys="expandedKeys"
        :data="data"
        rowKey="id"
        @expandChange="expandChange"
    >
        <FTableColumn v-slot="{ row }" type="expand">
            <FGrid
                :gutter="[20, 20]"
                wrap
                style="background: #f8f8f8; padding: 16px"
            >
                <FGridItem :span="12"> 省份:{{ row.province }} </FGridItem>
                <FGridItem :span="12"> 市区:{{ row.city }} </FGridItem>
                <FGridItem :span="12"> 邮编:{{ row.zip }} </FGridItem>
                <FGridItem :span="12"> 地址:{{ row.address }} </FGridItem>
            </FGrid>
        </FTableColumn>
        <FTableColumn prop="date" label="日期" />
        <FTableColumn prop="name" label="姓名" />
    </FTable>
    <FButton style="margin-top: 10px" @click="toggle">
        手动展开/关闭第一行
    </FButton>
</template>

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

export default {
    setup() {
        const expandedKeys = ref([1]);
        const tableRef = ref(null);
        const data = Array.from([1, 2, 3], (i) => {
            return {
                id: i,
                date: `2016-05-2016-05-2016-05-2016-05-2016-05-2016-05-2016-05-2016-05-${
                    i < 10 ? `0${i}` : i
                }`,
                name: '王小虎',
                province: '上海',
                city: '普陀区',
                address: '上海市普陀区金沙江路 1518 弄',
                zip: 200333,
            };
        });
        const expandChange = ({ row, expanded }) => {
            console.log(
                '[table.expand] [expandChange] row:',
                row,
                ' expanded:',
                expanded,
            );
        };
        const toggle = () => {
            tableRef.value.toggleRowExpend({ row: data[0] });
        };
        return {
            tableRef,
            data,
            toggle,
            expandChange,
            expandedKeys,
        };
    },
};
</script>

合并行或列

多行或多列共用一个数据时,可以合并行或列。

play
<template>
    <FTable :data="data" :span-method="objectSpanMethod" bordered verticalLine>
        <FTableColumn prop="id" label="ID" :width="180" />
        <FTableColumn prop="name" label="姓名" />
        <FTableColumn prop="amount1" label="数值 1(元)" />
        <FTableColumn prop="amount2" label="数值 2(元)" />
        <FTableColumn prop="amount3" label="数值 3(元)" />
    </FTable>
</template>

<script setup>
const data = [
    {
        id: '12987122',
        name: '王小虎',
        amount1: '234',
        amount2: '3.2',
        amount3: 10,
    },
    {
        id: '12987123',
        name: '王小虎',
        amount1: '165',
        amount2: '4.43',
        amount3: 12,
    },
    {
        id: '12987124',
        name: '王小虎',
        amount1: '324',
        amount2: '1.9',
        amount3: 9,
    },
    {
        id: '12987125',
        name: '王小虎',
        amount1: '621',
        amount2: '2.2',
        amount3: 17,
    },
    {
        id: '12987126',
        name: '王小虎',
        amount1: '539',
        amount2: '4.1',
        amount3: 15,
    },
];

const objectSpanMethod = ({ rowIndex, columnIndex }) => {
    if (columnIndex === 0) {
        if (rowIndex % 2 === 0) {
            return {
                rowspan: 2,
                colspan: 1,
            };
        } else {
            return {
                rowspan: 0,
                colspan: 0,
            };
        }
    }
    if (columnIndex === 4) {
        if (rowIndex === 0) {
            return {
                rowspan: 1,
                colspan: 1,
            };
        } else if (rowIndex % 2 !== 0) {
            return {
                rowspan: 2,
                colspan: 1,
            };
        } else {
            return {
                rowspan: 0,
                colspan: 0,
            };
        }
    }
};
</script>

无数据

play
<template>
    <FForm :labelWidth="160">
        <FFormItem label="展示类型:">
            <FRadioGroup v-model="emptyType">
                <FRadio value="normal">默认</FRadio>
                <FRadio value="custom">自定义</FRadio>
            </FRadioGroup>
        </FFormItem>
        <FFormItem label="表格边框:">
            <FRadioGroup
                v-model="bordered"
                :cancelable="false"
                :options="[
                    { label: '否(默认)', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem label="水平分割线:">
            <FRadioGroup
                v-model="horizontalLine"
                :cancelable="false"
                :options="[
                    { label: '', value: false },
                    { label: '是(默认)', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem label="垂直分割线:">
            <FRadioGroup
                v-model="verticalLine"
                :cancelable="false"
                :options="[
                    { label: '否(默认)', value: false },
                    { label: '', value: true },
                ]"
            />
        </FFormItem>
        <FFormItem label="间距大小:">
            <FRadioGroup v-model="size">
                <FRadio value="middle">middle(默认)</FRadio>
                <FRadio value="small">small</FRadio>
            </FRadioGroup>
        </FFormItem>
        <FFormItem label="自定义空数据文本:">
            <FInput v-model="emptyText" />
        </FFormItem>
    </FForm>

    <FDivider />

    <FTable
        v-if="emptyType === 'normal'"
        :data="data"
        :size="size"
        :bordered="bordered"
        :horizontalLine="horizontalLine"
        :verticalLine="verticalLine"
        :emptyText="emptyText || '暂无数据'"
    >
        <FTableColumn :width="200" prop="date" label="日期" />
        <FTableColumn :width="200" prop="name" label="姓名" />
        <FTableColumn :width="200" prop="address" label="地址" />
        <FTableColumn :width="200" prop="contact" label="联系人" />
        <FTableColumn :width="200" prop="postcode" label="邮编" />
    </FTable>

    <FTable
        v-if="emptyType === 'custom'"
        :data="data"
        :size="size"
        :bordered="bordered"
        :horizontalLine="horizontalLine"
        :verticalLine="verticalLine"
    >
        <template #empty>
            <FEmpty :description="emptyText" />
        </template>
        <FTableColumn :width="200" prop="date" label="日期" />
        <FTableColumn :width="200" prop="name" label="姓名" />
        <FTableColumn :width="200" prop="address" label="地址" />
        <FTableColumn :width="200" prop="contact" label="联系人" />
        <FTableColumn :width="200" prop="postcode" label="邮编" />
    </FTable>
</template>

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

const emptyType = ref('normal');
const size = ref('middle');
const emptyText = ref();

const bordered = ref(false);
const horizontalLine = ref(true);
const verticalLine = ref(false);

const data = reactive([]);
</script>

使用 columns 配置列

play
<template>
    <FTable
        rowKey="id"
        :data="data"
        bordered
        verticalLine
        layout="auto"
        :columns="columns"
    />
</template>

<script>
import { defineComponent, h } from 'vue';

export default defineComponent({
    setup() {
        const data = Array.from([1, 2, 3], (i) => {
            return {
                id: i,
                date: `2016-05`,
                name: '王小虎',
                province: '上海',
                city: '普陀区',
                address: '上海市普陀区金沙江路 1518 弄',
                zip: 200333,
            };
        });
        const columns = [
            {
                type: 'selection',
            },
            {
                prop: 'date',
                label: '日期',
                width: 150,
                render: ({ row }) => {
                    return h('span', row.date);
                },
                renderHeader: () => {
                    return h('span', { style: { fontSize: '20px' } }, '日期');
                },
            },
            {
                label: '配送信息',
                children: [
                    { prop: 'name', label: '姓名', width: 150 },
                    {
                        label: '地址信息',
                        children: [
                            { prop: 'province', label: '省份', width: 150 },
                            { prop: 'city', label: '市区', width: 150 },
                            { prop: 'address', label: '详细地址', width: 300 },
                        ],
                    },
                ],
            },
        ];
        return {
            data,
            columns,
        };
    },
});
</script>

列宽可拖拽配置

play
<template>
    <FTable
        :data="data"
        :columns="columns"
        layout="auto"
        @headerResize="handleHeaderResize"
    />
</template>

<script setup lang="ts">
import { reactive } from 'vue';

const data = reactive(
    Array.from([1, 2, 3], (i) => {
        return {
            date: `2016-05-${i < 10 ? `0${i}` : i}`,
            name: '王小虎',
            address: '上海市普陀区金沙江路 1516 弄',
        };
    }),
);

const columns = [
    {
        prop: 'date',
        label: '日期',
        minWidth: 200,
        resizable: true,
    },
    {
        prop: 'name',
        label: '姓名',
        minWidth: 200,
        resizable: true,
    },
    {
        prop: 'address',
        label: '地址',
        minWidth: 300,
    },
];

const handleHeaderResize = (...args: any[]) => {
    console.log('[FTable.headerResize]:', args);
};
</script>

FTable Props

属性说明类型可选值默认值
bordered是否展示表格边框boolean-false
horizontalLine是否展示水平分割线boolean-true
verticalLine是否展示垂直分割线boolean-false
rowClassName行的 className 的回调方法,也可以使用字符串为所有行设置一个固定的 className。string | object | array | ({ row, column, rowIndex, columnIndex, cellValue })=> ( object | array | string )--
rowStyle行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。object | ({row, rowIndex})=> object--
data数据array-[]
emptyText空数据时显示的文本内容,也可以通过 #empty 设置string-暂无数据
heighttable 的高度,如果内容过多超出时则表头固定,内容滚动number--
rowKey行数据的 Key,用来优化 Table 的渲染string | (row)=> string--
showHeader是否展示表头boolean-true
spanMethod合并行或列的计算方法({ row, column, rowIndex, columnIndex }) => { rowspan: string, colspan: string }--
virtualScroll是否启动虚拟滚动,当启用时不支持展开行boolean-false
sizetable 的间距大小stringmiddle smallmiddle
layouttable 列宽度分割算法,fixed为等分,auto按内容大小比例。只有不设置 height 和不启动虚拟滚动时才生效!stringauto fixedfixed
draggable是否开启拖拽,开启虚拟滚动后失效boolean-false
beforeDragend拖拽结束之前调用,当返回 false、Promise.resolve(false)、Promise.reject()时,拖拽会恢复之前的状态BeforeDragEnd-()= true
expandedKeys(v-model)展开的节点的 key 的数组Array<string | number>-[]
checkedKeys(v-model)勾选节点 key 的数组Array<string | number>-[]
columns列的配置信息Array<ColumnChildren>--
hoverable是否开启 hover 行样式boolean-true
striped是否开启斑马线条纹boolean-false
alwaysScrollbar是否总是显示滚动条boolean-false

FTable Slots

名称说明
empty自定义表格没有数据时的内容

FTable Events

事件名称说明回调参数
cellClick当某个单元格被点击时会触发该事件({row, column, cellValue, event})=> void
expandChange当用户对某一行展开或者关闭的时候会触发该事件({ row, expanded })=> void
headerClick当某一列的表头被点击时会触发该事件({column, event}) => void
headerResize当某一列的表头被拖拽时会触发该事件({current, columns,event}) => void
rowClick当某一行被点击时会触发该事件({row, event}) => void
select当用户手动勾选数据行的 Checkbox 时触发的事件({ selection, row, checked})=> void
selectAll当用户手动勾选全选 Checkbox 时触发的事件({ selection, checked }) => void
selectionChange当选择项发生变化时会触发该事件(selection) => void
dragstart拖拽开始触发(event, item, index) => void
dragend拖拽结束触发(event, item, index) => void
sortChange点击排序后触发({prop?: string; order?: 'descend' | 'ascend'; sorter?: Function | 'default'}) => void
afterSort排序完成后触发(data) => void

FTable Methods

名称说明参数
clearSelection用于多选表格,清空用户的选择() => void
toggleRowSelection用于多选表格,切换某一行的选中状态({row: RowType})=> void
toggleAllSelection用于多选表格,切换全选和全不选() => void
toggleRowExpend用于控制某行的展开隐藏({row: RowType})=> void
sort设定表格的排序状态(prop: string, order: 'ascend' | 'descend' | false) => void
clearSorter清空所有排序状态() => void

FTableColumn Props

属性说明类型可选值默认值
action操作array | object--
align对齐方式stringleft / center / rightleft
colClassName列的 className 的回调方法,也可以使用字符串为所有行设置一个固定的 className。string | object | array | ({ row, column, rowIndex, columnIndex, cellValue })=> ( object | array | string )--
colStyle列的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。object | ({ row, column, rowIndex, columnIndex, cellValue }) => object--
fixed列是否固定在左侧或者右侧,true 表示固定在左侧string | booleantrue / left / right-
formatter用来格式化内容({row, column, rowIndex, columnIndex, cellValue}) => any--
label列的标题,也可以使用 #header 自定义string--
minWidth列最小的宽度,如果容器宽度够大,则会自适应补偿number--
prop列内容的字段名string--
selectable仅对 type=selection 的列有效,Function 的返回值用来决定这一行的 CheckBox 是否可以勾选({row, rowIndex}) => boolean--
type列的类型,如果设置为selection则显示选择器,如果设置为expand则显示一个展开按钮stringdefault / selection / expanddefault
multiple列选择类型,type为selection时,可以设置单选和多选行模式stringbooleantrue
width对应列的宽度,优先级大于 minWidthnumber--
ellipsis设置宽度后,如果文本溢出后出现省略号,设置为对象时参考 Ellipsis 组件配置boolean | Object-false
visible是否显示列boolean-true
sortable是否排序列boolean-false
sorter排序方法,如果设为 'default' 表格将会使用一个内置的排序函数;其他工作的方式类似 Array.sort 的对比函数((a: RowType, b: RowType) => boolean) | 'default'-default
sortDirections支持的排序方式string[]-['ascend', 'descend']
resizable列是否可设置大小boolean-false

FTableColumn Slots

名称说明类型
default自定义列的内容({ row, column, rowIndex, columnIndex, cellValue })=> VNode[]
header自定义表头的内容({ column, columnIndex }) => VNode[]

ColumnChildren

ColumnChildrenFTableColumn Props 保持一致,使用 render 函数替代FTableColumn组件的插槽。

名称说明类型
render自定义列的内容({ row, column, rowIndex, columnIndex, cellValue }) => VNode[]
renderHeader自定义表头的内容({ column, columnIndex }) => VNode[]