1、版本
虚拟表格组件要求组件库最低版本为2.2.0 虚拟表格
2、了解主要属性、API
Table属性 | 说明 | 默认值 |
---|---|---|
header-height | Header 的高度由height 设置。 如果传入数组,它会使 header row 等于数组长度 | 50 |
row-height | 每行的高度, 用于计算表的总高度 | 50 |
columns | 列 column 的配置数组 | - |
data | 要在表中渲染的数据数组 | - |
fixed | 单元格宽度是自适应还是固定 | false |
width | 表格宽度 | - |
height | 表格高度 | - |
column属性 | 说明 | 默认值 |
---|---|---|
align | 表格单元格内容对齐方式 | left |
hidden | 此列是否不可见 | - |
style | 自定义列单元格的类名,将会与 gird 单元格合并 | - |
title | Header 头部单元格中的默认文本 | - |
width | 列宽度 | - |
cellRenderer | 自定义单元格渲染器 | - |
headerCellRenderer | 自定义头部渲染器 | - |
插槽 | 参数 |
---|---|
row | RowSlotProps |
cell | CellSlotProps |
3、使用
<te-table-v2
v-if="!loading"
fixed
stripe
:row-height="40"
:header-height="40"
:columns="columns"
:data="dataSource"
:width="width"
:height="height"
>
<template #row="props">
<Row v-bind="props" />
</template>
</te-table-v2>
列渲染
/**
* 列
*/
const columns = ref<Column<any>[]>([
{
key: 'text',
dataKey: 'text',
title: '测试文本',
width: 90,
cellRenderer: ({ cellData: text }: any) => `测试-${text}`,
rowSpan: ({ rowIndex }: any) => {
const row = dataSource.value[rowIndex];
const firstIndex = dataSource.value?.findIndex((item) => item.month === row.month);
return firstIndex === rowIndex ? monthCountMap.value.get(row.month + '') ?? 1 : 0;
},
},
{
key: 'name',
dataKey: 'name',
title: '测试',
hidden: currentType.value,
width: 400,
cellRenderer: ({ cellData: name, rowData: row }: any) =>
editing.value
? h(
ElSelect,
{
modelValue: row.id,
clearable: true,
placeholder: '请选择',
['onUpdate:modelValue']: (value: number) => {
row.id = value;
},
onChange: () => handleInputChange(),
},
() =>
list.value?.map((item) =>
h(ElOption, {
value: item.id,
label: item.name,
}),
) ?? [],
)
: h(
'span',
{},
{
default: () => (row?.name ? row?.name : '-'),
},
),
},
{
key: 'count',
dataKey: 'count',
title: '数值',
align: 'right',
width: 160,
cellRenderer: ({ cellData: count, rowData: row }: any) =>
editing.value
? withDirectives(
h(TeInput, {
modelValue: count,
clearable: true,
placeholder: '请输入',
['onUpdate:modelValue']: (value: string) => {
row.count = value;
},
onInput: () => handleInputChange(),
}),
[[inputFilterDirective, { decimal: 2 }, 'number']],
)
: h(
'span',
{},
{
default: () => (row?.count ? row?.count : '-'),
},
),
},
{
key: 'operate',
dataKey: 'operate',
title: '操作',
width: 64,
cellRenderer: ({ rowData: row, rowIndex: rowIndex }: any) =>
h(
ElButton,
{
type: 'primary',
link: true,
disabled: mapDeleteDisabled(row),
onClick: () => handleSingleDelete(row, rowIndex),
},
{
default: () => '删除',
},
),
},
{
key: 'operate-2',
dataKey: 'operate-2',
title: '操作',
width: 64,
cellRenderer: ({ rowData: row }: any) =>
h(
ElButton,
{
type: 'primary',
link: true,
onClick: () => handleSingleAdd(row),
},
{
default: () => '新增',
},
),
rowSpan: ({ rowIndex }: any) => {
const row = dataSource.value[rowIndex];
const firstIndex = dataSource.value?.findIndex((item) => item.month === row.month);
return firstIndex === rowIndex ? monthCountMap.value.get(row.month + '') ?? 1 : 0;
},
headerCellRenderer: () => h('span', { default: () => '' }),
},
]);
合并单元格
const Row = ({ rowData, rowIndex, cells, columns }: any) => {
const rowSpan = columns[0].rowSpan({ rowData, rowIndex });
if (rowSpan > 1) {
const cell = cells[0];
const style = {
...cell.props.style,
height: `${rowSpan * 40 - 1}px`,
alignSelf: 'flex-start',
zIndex: 1,
backgroundColor: '#ffffff',
};
cells[0] = cloneVNode(cell, { style });
}
return cells;
};
4、难点
1、hidden控制列隐藏 如果在初始化赋值columns设置了hidden等于一个表达式,重新渲染时是不会生效的,我是在重新渲染时给hidden重新赋值了,可以试试hidden赋值一个箭头函数
2、rowSpan字段是控制表格单元格合并的,支持传入一个函数返回rowSpan
3、单元格动态渲染cellRenderer需要返回VNode 这里可以去学习下Vue中h函数的使用,我这里用到了普通文本、输入框、下拉框的渲染
// 完整参数签名
function h(
type: string | Component,
props?: object | null,
children?: Children | Slot | Slots
): VNode
第一个参数既可以是一个字符串 (用于原生元素) 也可以是一个 Vue 组件定义。第二个参数是要传递的 prop,第三个参数是子节点。 当创建一个组件的 vnode 时,子节点必须以插槽函数进行传递。如果组件只有默认槽,可以使用单个插槽函数进行传递。否则,必须以插槽函数的对象形式来传递。
其中比较难的是h函数中指令的使用,Vue提供了一个withDirectives的API
function withDirectives(
vnode: VNode,
directives: DirectiveArguments
): VNode
// [Directive, value, argument, modifiers]
type DirectiveArguments = Array<
| [Directive]
| [Directive, any]
| [Directive, any, string]
| [Directive, any, string, DirectiveModifiers]
>
用自定义指令包装一个现有的 vnode。第二个参数是自定义指令数组。每个自定义指令也可以表示为 [Directive, value, argument, modifiers]
形式的数组。如果不需要,可以省略数组的尾元素。
比如这里我用了我自定义的指令inputFilterDirective,后面两个参数是我给指令的传参
[[inputFilterDirective, { decimal: 2 }, 'number']],
转换到template模板语法就是v-inputFilterDirective:number="{ decimal: 2 }"
4、使用row插槽实现单元格合并以及自定义样式
const Row = ({ rowData, rowIndex, cells, columns }: any) => {
return cells
}
搭配row插槽使用
<template #row="props">
<Row v-bind="props" />
</template>
5、问题
使用的过程中也发现了几个问题 1、貌似不支持斑马纹,如果设置了stripe="true",合并的单元格依然是白色背景,而没有合并的单元全都是灰色
2、已合并行的单元格在滚动到顶部是只留下几行在可视区域,会出现不合并的情况。