holyes-table
v1.0.19
Published
合力思虚拟滚动表格组件
Readme
组件说明
这个基于Canvas计算行高的虚拟滚动表格组件。
目录
优化
用div+grid布局, 每一行的行高是通过Canvas计算的, 这样不会触发重排。
大数据时, 会通过worker线程计算高度,然后逐渐渲染最大高度,提升首次渲染性能。
表格支持激活时,保持滚动条位置。
影响滚动性能的自定义插槽,增加配置lazyLoadSlot=true,用于延迟加载插槽内容,增加滚动性能。
支持:
- 横向/纵向虚拟滚动
- 排序(包括树结构)
- 筛选(包括树结构)
- 固定列
- 复选框列(多列相互独立)
- 序号列(包括树结构)
- 拖动调整列宽度
- 行高自适应
- 表头分组
- 表尾行
- 树结构数据
- 展开行
- 自定义插槽(尽量少用,影响滚动性能;但支持懒加载插槽,滚动会更流畅;插槽需指定行高)
- 内置一些常用插槽,性能更好,如
- a标签
- tag标签
- 图标 (vxe-pc-ui图标)
- 按钮组
- 开关
注意事项
1 自定义插槽
- 已经默认配置了一些单元格类型 defaultType,不一定都需要自定义插槽。
- 如果确实需要自定义插槽,必须填写插槽的高度defaultHeight,最好定死列宽度,因为整个表格的高度是根据 Canvas 计算的,能准确计算出每一行的行高。
2 性能优化
- 大数据量时建议设置
lazyLoadSlot=true延迟加载自定义插槽内容 - 避免在自定义插槽中执行复杂计算或频繁更新的操作
- 合理使用
cellMinHeight控制最小行高,避免行高计算不准确
3 样式定制
- 表格使用 CSS Grid 布局,可以通过 CSS 变量自定义样式
- 支持斑马纹、悬停高亮、当前行高亮等样式配置
- 表头支持固定定位,滚动时保持可见
- 暂不支持自定义文字大小,会影响计算行高
组件依赖
- vxe-pc-ui ^4.9.41
- xe-utils ^4.0.4
基本用法
安装
安装组件
pnpm i holyes-table引入样式
// main.ts import 'holyes-table/style.css'引入组件
// 组件中引入 // xxx.vue import HolyesTable from "holyes-table" // 或者全局安装,后续就不用引入了 // main.ts import { createApp } from 'vue' import app from './app.vue' import HolyesTable from "holyes-table" createApp(app).use(HolyesTable).mount('#app')
导入类型(TS 项目)
import HolyesTable, {
HolyesTableColumnPropsType,
HolyesTableFooterProps,
HolyesTableTreeConfig,
HolyesTableExpandConfig,
HolyesTableFooterFieldTypes,
HolyesTableFooterType,
HolyesTableFooterDataType,
HolyesTableSlotDefaultType,
HolyesTableFilterType,
HolyesTableFilterlistOptions,
} from "holyes-table"简单示例
<template>
<HolyesTable
ref="tableRef"
:columns="columns"
:height="400"
:stripe="true"
:row-config="{ isCurrent: true, isHover: true }"
:showFooter="true"
:footerProps="footerProps"
></HolyesTable>
</template>
<script setup>
import { ref,defineAsyncComponent } from 'vue'
import HolyesTable, { HolyesTableColumnPropsType, HolyesTableFooterProps } from "holyes-table"
const tableRef = ref()
/** 表格列配置 */
const columns:HolyesTableColumnPropsType[] = [
{ field: 'name', title: '姓名', width: 100 },
{ field: 'age', title: '年龄', width: 80 },
{ field: 'address', title: '地址', width: 200 }
]
const tableData = [
{ name: '张三', age: 25, address: '北京市朝阳区' },
{ name: '李四', age: 30, address: '上海市浦东新区' }
]
/** 表尾行属性配置 */
const footerProps: HolyesTableFooterProps[] = [
// 表尾第一行
{
height: 30,
fieldTypes: [ // 表尾行字段类型, 顺序没有影响
{
field: "name",
type: "文本", // 文本, 会直接使用text属性
text: "小计"
},
{
field: "salary",
type: "合计", // 合计会自动计算
dataType: "number2"
}
]
}
]
// 重新设置数据
tableRef.value.reloadData(tableData)
</script>Props 属性
| 属性名 | 类型 | 默认值 | 说明 | | -------- | ------ | -------- | -------- | | columns | Array< HolyesTableColumnPropsType> | [] | 表格列配置 | | height | Number | - | 表格高度 | | cellMinHeight | Number | 22 | 单元格最小行高 | | loading | Boolean | false | 是否显示加载状态 | | loadingConfig | Object | {} | 加载配置,包含text属性 | | lazyLoadSlot | Boolean | false | 自定义插槽是否懒加载,默认否;开启后,自定义插槽多时滚动会更流畅 | | rowConfig | Object | {} | 行配置,包含isCurrent(是否高亮当前行)和isHover(是否高亮悬停行) | | sortConfig | Object | {} | 排序配置,包含trigger(触发方式:cell(默认点击单元格排序)/default(默认点击图标排序)) | | stripe | Boolean | true | 是否显示斑马纹背景 | | showOverflow | String | "" | 是否显示溢出内容,可选值:title/tooltip/ellipsis/空 | | showFooter | Boolean | false | 是否显示表尾行 | | footerProps | Array< HolyesTableFooterProps> | [] | 表尾行属性配置 | | treeConfig | HolyesTableTreeConfig | {} | 树形结构配置项,详见下方说明 | | expandConfig | HolyesTableExpandConfig | {} | 展开行配置项,详见下方说明 |
列配置说明 (columns)
列配置是表格的核心配置,支持以下属性:
interface HolyesTableColumnPropsType {
/** 字段名 */
field: string
/** 列标题 */
title: string
/** 列宽度 */
width?: number
/** 列最小宽度 */
minWidth?: number
/** 列固定位置 */
fixed?: "left" | "right" | ""
/** 列是否可排序 */
sortable?: boolean
/** 列排序实际的字段,默认值为列的field */
sortField?: string
/** 列类型 */
type?: "seq" | "checkbox" | "expand"
/** 列样式 */
style?: any
/** 列是否可筛选 */
isFilter?: {
/** 筛选的类型 */
type: HolyesTableFilterType
/** 筛选type是listFilter时,筛选选项数组 */
listOptions?: { label: string; value: HolyesTableFilterlistOptions }[]
}
/** 列是否可调整宽度 */
resizable?: boolean
/** 列插槽 */
slots?: {
/** 列自定义插槽 */
default?: string
/** 列自定义插槽高度,有插槽的话必填,插槽单元格不自动换行 */
defaultHeight?: number
/** 列插槽类型 */
defaultType?: HolyesTableSlotDefaultType
/** 列插槽默认属性 */
defaultProps?: {
/** 标签 插槽默认属性 */
tag?: {
/** 标签匹配规则 */
options?: {
/** 标签状态 */
status?: "primary" | "success" | "info" | "warning" | "error"
/** 标签颜色 */
color?: any
/** 标签显示的文本 */
label?: string
/** 标签对比的值 */
value: any
}[]
}
/** 图标 插槽默认属性 */
icon?: {
/** 图标名称, 参考 vxe-icon 图标名称 https://vxeui.com/#/component/icon/base */
name?: VxeIconPropTypes.Name
/** 图标样式 */
style?: any
}
/** 按钮 插槽默认属性 */
buttons?: {
/** 按钮组数组, 每个元素是一行div的按钮数组 */
options?: {
/** 按钮内容*/
title?: string
/** 按钮提示 */
tip?: string
/** 按钮前缀图标 */
prefixIcon?: any
/** 按钮大小 */
size?: "medium" | "small" | "mini"
/** 按钮类型 */
type?: "primary" | "danger"
/** 按钮样式, 默认button */
mode?: "text" | "button"
/** 按钮是否显示,取决于row的哪个字段 */
showField?: string
/** 按钮是否禁用,取决于row的哪个字段 */
disabledField?: string
/** 按钮是否加载中,取决于row的哪个字段 */
loadingField?: string
/** 按钮点击事件 */
onClick?: (row: any, index: number) => void
/** 其他参数 */
params?: any
}[][]
}
/** 开关 插槽默认属性 */
switch?: {
/** 开关选中时显示的文本 */
checkedChildren?: string
/** 开关选中时显示的文本 */
openLabel?: string
/** 开关未选中时显示的文本 */
uncheckedChildren?: string
/** 开关未选中时显示的文本 */
closeLabel?: string
/** 开关选中时的值 */
checkedValue?: any
/** 开关未选中时的值 */
uncheckedValue?: any
/** 开关选中时的值 */
openValue?: any
/** 开关未选中时的值 */
closeValue?: any
/** 按钮是否显示,取决于row的哪个字段 */
showField?: string
/** 按钮是否禁用,取决于row的哪个字段 */
disabledField?: string
/** 按钮是否加载中,取决于row的哪个字段 */
loadingField?: string
/** 开关点击事件 */
onChange?: (row: any, index: number) => void
}
checkbox?: {
/** 多选框是否半选,取决于row的哪个字段 */
checkedIndeterminateField?: string
/** 多选框是否禁用,取决于row的哪个字段 */
disabledField?: string
/** 多选框点击事件 */
onChange?: (row: any, index: number) => void
}
}
/** 列表头插槽 */
header?: string
/** 列表头插槽类型 */
headerType?: "a"
/** 表尾插槽 */
footer?: string
}
footer?: {
/** 表尾行高度 */
height?: number
/** 表尾类型 */
type?: HolyesTableFooterType
dataType?: "number0" | "number1" | "number2" | "string"
/** 表尾文本 */
text?: string
}[]
/** 表头分组 */
children?: HolyesTableColumnPropsType[]
/** 只对 treeConfig 配置时有效,指定为树节点 */
treeNode?: boolean
}筛选字段类型说明
isFilter 属性中的类型定义:
/** 筛选字段类型 */
type HolyesTableFilterType = "inputFilter" | "numberFilter" | "numberFilter%" | "listFilter" | ""
/** 筛选字段列表选项 */
type HolyesTableFilterlistOptions = string | number | boolean- inputFilter:输入筛选,模糊/精确匹配
- numberFilter:数字筛选,支持 >/≥/=/</≤
- numberFilter%:数字百分比筛选
- listFilter:列表筛选,多选匹配
列配置示例
const columns = [
// 序号列
{ type: 'seq', title: '序号', width: 60 },
// 多选列
{ type: 'checkbox', title: '选择', width: 60 },
// 普通列
{ field: 'name', title: '姓名', width: 100, sortable: true },
// 固定列
{ field: 'age', title: '年龄', width: 80, fixed: 'left' },
// 自定义插槽列
{
field: 'status',
title: '状态',
width: 100,
slots: {
default: 'statusSlot',
defaultHeight: 32
}
},
// 默认插槽类型列
{
field: 'link',
title: '链接',
width: 120,
slots: {
defaultType: 'a'
}
},
// 表头分组
{
title: '联系信息',
children: [
{ field: 'phone', title: '电话', width: 120 },
{ field: 'email', title: '邮箱', width: 150 }
]
}
]树形结构配置 (treeConfig)
interface HolyesTableTreeConfig {
/** 是否开启树形结构功能 */
isOpenTree: boolean
/** 自动将列表转为树结构 */
transform?: boolean
/** 节点id字段, 默认是id */
idField?: string
/** 父节点字段, 默认是parentId */
parentField?: string
/** 子节点字段, 默认是children */
childrenField?: string
/** 树节点的缩进, 默认是20px */
indent?: number
/** 展开/收起的触发方式, 默认是default(点击按钮触发) */
trigger?: 'default' | 'cell' | 'row'
/** 是否懒加载 */
lazy?: boolean
/** 只对 lazy 启用后有效,标识是否存在子节点,从而控制是否允许被点击, 默认是hasChild */
hasChildField?: string
/** 懒加载方法 */
loadMethod?: (node: any) => any
/** 是否保留展开状态, 默认是false */
reserve?: boolean
/** 是否显示树形图标, 默认是true */
showIcon?: boolean
/** 多选框列, 是否父子关联选择, 默认是true */
checkStrictly?: boolean
}树形结构配置示例
const treeConfig = {
isOpenTree: true, // 开启树形结构
transform: true, // 自动将列表转为树结构
idField: 'id', // 节点id字段, 保留展开状态时必填
parentField: 'parentId', // 父节点字段
childrenField: 'children', // 子节点字段
indent: 20, // 缩进像素
trigger: 'default', // 点击按钮触发展开/收起
showIcon: true // 显示树形图标
reserve: false // 保留展开状态
}展开行配置 (expandConfig)
interface HolyesTableExpandConfig {
/** 是否开启展开行功能 */
isExpand: boolean
/** 展开行高度 (像素), 必填 */
height: number
/** 展开/收起的触发方式,默认是 default(点击按钮触发) */
trigger?: 'default' | 'cell' | 'row'
/** 展开行模式,默认是 undefined(展开行随着 x 轴滚动条滚动);
* inside(不随着 x 轴滚动条滚动) */
mode?: 'inside' | undefined
/** 插槽名称 */
slotName: string
}展开行配置示例
const expandConfig = {
isExpand: true, // 开启展开行功能
height: 100, // 展开行高度(必填)
trigger: 'default', // 点击按钮触发展开/收起
mode: undefined, // 展开行随着 x 轴滚动条滚动
slotName: 'expandSlot' // 展开行插槽名称
}表尾行属性配置 (footerProps)
/** 表尾类型 */
type HolyesTableFooterType = "合计" | "文本"
/** 表尾行数据类型 */
type HolyesTableFooterDataType = "number0" | "number1" | "number2" | "string"
/** 表尾行字段类型 */
type HolyesTableFooterFieldTypes = {
/** 表尾行字段 */
field: string
/** 表尾行类型 */
type: HolyesTableFooterType
/** 表尾行文本(type为"文本"时使用) */
text?: string
/** 表尾行数据类型(type为"合计"时使用) */
dataType?: HolyesTableFooterDataType
}
/** 外面传入的表尾行属性 */
type HolyesTableFooterProps = {
/** 表尾行高度 */
height?: number
/** 表尾行字段类型数组 */
fieldTypes?: HolyesTableFooterFieldTypes[]
}- 合计:自动计算该列的合计值,需指定
dataType - 文本:直接显示
text属性的文本内容 - dataType 说明:
number0:整数number1:保留1位小数number2:保留2位小数string:文本
Emits 事件
| 事件名 | 参数 | 说明 | | -------- | ------ | -------- | | headerCellClick | (column: HolyesTableColumnPropsType) | 点击表头单元格 | | cellClick | (row: any, column: HolyesTableColumnPropsType) | 点击单元格 | | currentChange | (currentRow: any, oldCurrentRow: any) | 当前行变化 | | checkboxChange | (checked: boolean, row: any) | 多选列变化 | | checkboxAll | (checked: boolean) | 多选列全选 | | resizableChange | (column: HolyesTableColumnPropsType, width: number) | 列调整宽度 | | toggleRowExpand | (rows: any[], expanded: boolean) | 展开/收起行 |
Methods 方法
通过 ref 可以调用以下组件方法:
| 方法名 | 参数 | 返回值 | 说明 |
| -------- | ------ | -------- | -------- |
| reloadData | (data: any[]) | Promise<void> | 重新设置表格数据 |
| getTableData | () | { fullData, visibleData } | 获取表格数据(包含全部数据、当前可见数据) |
| getCheckboxRecords | (isFull: boolean, field?: string) | any[] | 获取所有选中的行数据,isFull 为false时, 只获取当前可见数据的选中行; field 为选中列字段, 默认第一个选中列字段 |
| clearCheckboxRow | () | void | 清空多选列选中状态 |
| updateFooter | () | void | 更新表尾行数据 |
| updateFooterCellLabel | (field: string, rowIndex: number, label: string) | void | 更新表尾单元格文本 |
| setTreeExpand | (rows: any[], expanded: boolean) | void | 设置树形节点展开/收起 |
| setAllTreeExpand | (isExpand: boolean) | void | 设置所有树形节点展开/收起,不包含懒加载节点 |
| clearTreeExpand | () | void | 清除所有树形节点展开状态 |
| setRowExpand | (rows: any[], expanded: boolean) | void | 设置展开行展开/收起 |
方法使用示例
<template>
<HolyesTable
ref="pretextTableRef"
:columns="columns"
:data="tableData"
/>
<button @click="handleRefresh">刷新数据</button>
<button @click="handleGetSelected">获取选中行</button>
<button @click="handleClearExpand">清除树展开</button>
</template>
<script setup>
import { ref } from 'vue'
const pretextTableRef = ref()
const tableData = ref([
{ id: 1, name: '张三', parentId: null },
{ id: 2, name: '李四', parentId: 1 }
])
/** 重新加载数据 */
const reloadData = () => {
pretextTableRef.value?.reloadData(tableData.value)
}
/** 获取表格数据 */
const getTableData = () => {
const tableData = pretextTableRef.value?.getTableData()
console.log('全部数据:', tableData.fullData)
console.log('当前可见数据:', tableData.visibleData)
}
/** 获取所有选中的行数据 */
const getCheckboxRecords = () => {
const selectedRows = pretextTableRef.value?.getCheckboxRecords(true, 'checkbox') || []
console.log('选中的行:', selectedRows)
}
/** 清空多选列选中状态 */
const clearCheckboxRow = () => {
pretextTableRef.value?.clearCheckboxRow()
}
/** 更新表尾 */
const updateFooter = () => {
pretextTableRef.value?.updateFooter()
}
/** 更新表尾单元格文本 */
const updateFooterCell = () => {
pretextTableRef.value?.updateFooterCellLabel('name', 0, '合计')
}
/** 设置树形节点展开 */
const setExpandTree = (row) => {
pretextTableRef.value.setTreeExpand([row], true)
}
/** 清除树形展开状态 */
const clearTreeExpand = () => {
pretextTableRef.value?.clearTreeExpand()
}
/** 设置所有树形节点展开/收起 */
const setAllTreeExpand = (isExpand: boolean) => {
pretextTableRef.value.setAllTreeExpand(isExpand)
}
/** 设置展开行 */
const setExpandRow = (row,expanded: boolean) => {
pretextTableRef.value.setRowExpand([row], expanded)
}
</script>插槽使用
自定义插槽
<HolyesTable
:columns="columns"
:data="tableData"
>
<!-- 自定义状态插槽 -->
<template #status_default="{ row, column }">
<span :class="`status-${row.status}`">
{{ row.status === 'active' ? '活跃' : '禁用' }}
</span>
</template>
<!-- 自定义表头插槽 -->
<template #status_header="{ column }">
<div style="display: flex; align-items: center;">
<span>{{ column.title }}</span>
<vxe-icon name="question" style="margin-left: 4px;" />
</div>
</template>
<!-- 自定义底部插槽 -->
<template #status_footer="{ column }">
<span>123</span>
</template>
</HolyesTable>
<script setup>
const columns = [
// 自定义插槽
{
field: 'status',
title: '状态',
width: 100,
slots: {
header: 'status_header',
default: 'status_default',
defaultHeight: 32, // 必须指定插槽高度
footer: "status_footer"
}
}
]
</script>默认插槽类型
组件内置了多种默认插槽类型,无需自定义插槽:
- 链接类型 (a): 自动渲染为链接样式
- 标签类型 (tag): 自动渲染为标签样式
- 图标类型 (icon): 自动渲染为图标
- 按钮组类型 (buttons): 自动渲染为按钮组
- 开关类型 (switch): 自动渲染为开关
- 复选框类型 (checkbox): 自动渲染为多选框
const columns = [
// 链接类型
{
field: 'link',
title: '链接',
slots: {
defaultType: 'a' // 自动渲染为链接
}
},
// 标签类型
{
field: 'tags',
title: '标签',
slots: {
defaultType: 'tag', // 自动渲染为标签
defaultProps: {
tag: {
options: [
{ label: '活跃', value: '1', status: 'success' },
{ label: '禁用', value: '0', status: 'warning' }
]
}
}
}
},
// 图标类型
{
field: 'icon',
title: '图标',
slots: {
defaultType: 'icon',
defaultProps: {
icon: {
name: 'success', // 图标名称
style: { color: 'green' } // 图标样式
}
}
}
},
// 按钮组类型
{
field: 'buttons',
title: '操作',
slots: {
defaultType: 'buttons',
defaultProps: {
buttons: {
options: [
[
{
title: '编辑',
type: 'primary',
onClick: (row, index) => {
console.log('编辑', row)
}
},
{
title: '删除',
type: 'danger',
onClick: (row, index) => {
console.log('删除', row)
}
}
]
]
}
}
}
},
// 开关类型
{
field: 'status',
title: '状态',
slots: {
defaultType: 'switch',
defaultProps: {
switch: {
checkedChildren: '开',
uncheckedChildren: '关',
checkedValue: true,
uncheckedValue: false,
onChange: (row, index) => {
console.log('开关变化', row)
}
}
}
}
},
// 复选框类型
{
field: 'checkbox',
title: '多选',
slots: {
defaultType: 'checkbox',
defaultProps: {
checkbox: {
checkedIndeterminateField: 'halfChecked',
disabledField: 'checkedDisabled',
onChange: (row, index) => {
console.log('复选框变化', row)
}
}
}
}
}
]