组件库TDesign的表格<t-table>的使用,行列合并以及嵌入插槽实现图标展示,附踩坑
碎碎念:有点难用,不丝滑(以下介绍的难点不是真的难,只是有点点点难用)
背景:需要实现表格的行列合并以及图标的嵌入,想到使用组件库组件来方便开发
链接:TDesign Web Vue Next
难点1:数据的定义
看到官方的代码实现,定义结构感觉有点乱
官方示例图:
官方代码:
这部分主要就是定义table结构,
data:表格数据;columns:表头结构
rowkey要定义,不然错误信息
rowspan-and-colspan:定义表格行列合并的规则(合并的框会框会覆盖后面的)
<template>
<div>
<t-table
:bordered="true"
:data="data"
:columns="columns"
row-key="i"
:rowspan-and-colspan="rowspanAndColspan"
resizable
table-layout="fixed"
lazy-load
/>
</div>
</template>
然后是js里的
statusNameListMap定义的是type有关样式值,存关键的对象
data用new Array生成
colums里定义表头的结构,colspan定义的是列合并
rowspanAndColspan = ({ col,row, ,colIndex , rowIndex })
其中 col 是表头信息, row是一行的信息 ,colIndex是单元格列 index, rowIndex是单元格行 index
<script setup lang="jsx">
import { ErrorCircleFilledIcon, CheckCircleFilledIcon, CloseCircleFilledIcon } from 'tdesign-icons-vue-next';
const statusNameListMap = {
0: { label: '审批通过', theme: 'success', icon: <CheckCircleFilledIcon /> },
1: { label: '审批失败', theme: 'danger', icon: <CloseCircleFilledIcon /> },
2: { label: '审批过期', theme: 'warning', icon: <ErrorCircleFilledIcon /> },
};
const data = new Array(6).fill(null).map((_, i) => ({
i,
status: i % 3,
applicant: ['贾明', '张三', '王芳'][i % 3],
channel: ['电子签署', '纸质签署', '纸质签署'][i % 3],
type: ['审批通过', '已过期', '审批失败', '审批中'][i % 4],
detail: {
email: [
'w.cezkdudy@lhll.au',
'r.nmgw@peurezgn.sl',
'p.cumx@rampblpa.ru',
'b.nmgw@peurezgn.sl',
'd.cumx@rampblpa.ru',
][i % 5],
},
needed: ['Y', 'N'][i % 1],
description: ['宣传物料制作费用', 'algolia 服务报销', '相关周边制作费', '激励奖品快递费'][i % 4],
createTime: '2021-11-01',
}));
const columns = [
{ colKey: 'applicant', title: '申请人', width: '100' },
{
colKey: 'status',
title: '申请状态',
width: '150',
cell: (h, { row }) => {
return (
<t-tag shape="round" theme={statusNameListMap[row.status].theme} variant="light-outline">
{statusNameListMap[row.status].icon}
{statusNameListMap[row.status].label}
</t-tag>
);
},
},
{
colKey: 'description',
title: '审批事项',
width: 150,
},
{
colKey: 'detail.email',
title: '邮箱地址',
},
{
colKey: 'channel',
// 多行表头合并请参考「多级表头示例」
title: '其他信息',
// 仅适用于单行表头合并列
colspan: 2,
// 设置列样式,注释的示例代码有效
// attrs: ({ type, col, row, colIndex, rowIndex }) => ({
// style: {
// color: 'blue',
// },
// }),
},
{
colKey: 'createTime',
title: '创建时间',
},
];
const rowspanAndColspan = ({ col, rowIndex, colIndex }) => {
if (colIndex === 0 && rowIndex % 2 === 0) {
return {
rowspan: 2,
};
}
if (col.colKey === 'description' && rowIndex === 1) {
return {
colspan: 2,
rowspan: 2,
};
}
if (col.colKey === 'email' && rowIndex === 4) {
return {
colspan: 2,
rowspan: 2,
};
}
};
</script>
打印了下data的结构:发现每个字段都有,是个数组对象,所以合并的时候数据是会占位,并被前面的覆盖掉
难点二:样式修改
官方的样式只提供了表格的样式修改
1. 行的类名rowClassName设置
2. 列的类名className设置
3. attr: { style: { } }设置该列的内联样式
4. cell:表格的渲染函数,可自定义插槽作为该列,比如增加图标
5. title:表头的渲染函数,可自定义插槽作为表头,比如增加图标
6. render:可以渲染表头,也可以渲染单元格,相当于cell或title
官方代码:我这里只放部分对应的
// 第一种 (王芳那一行)
<t-table row-key="id" :data="data" :columns="columns" :row-class-name="getRowClassName">
<template #footerSummary>
<div class="t-table__row-filter-inner"><InfoCircleIcon />近期申请耗时较长</div>
</template>
</t-table>
const getRowClassName = ({ rowIndex }) => {
if (rowIndex === 2) return 'custom-third-class-name';
return '';
};
.t-demo__style .t-table .custom-third-class-name > td {
background-color: var(--td-brand-color-light);
font-weight: bold;
}
// 第二种(第四列)
{
colKey: 'channel',
title: '签署方式',
width: 120,
align: 'right',
className: () => {
return 'custom-cell-class-name';
},
},
.t-table td.custom-cell-class-name {
color: orange;
font-weight: bold;
}
// 第三种 (第三列)
{
colKey: 'time',
title: '申请耗时(天)',
width: 120,
align: 'center',
// 设置单元格类名
className: ({ row }) => {
if (row.time >= 9) {
return 'custom-cell-class-name';
}
return '';
},
attrs: ({ row }) => {
if (row.time >= 9) {
return {
style: {
fontWeight: 600,
backgroundColor: 'var(--td-warning-color-light)',
},
};
}
},
},
<t-table :data="data" :columns="columns" row-key="property" lazy-load>
<!-- 自定义表头,title值为插槽名称 -->
<template #title-slot-name>
<div style="display: flex; align-items: center">
<UserCircleIcon style="margin-right: 8px" />申请人</div>
</template>
</t-table>
// cell colums里的 对应审批状态的单元格
{
title: '审批状态',
colKey: 'status',
// 使用 cell 方法自定义单元格:
cell: (h, { row }) => {
return (
<t-tag shape="round" theme={statusNameListMap[row.status].theme} variant="light-outline">
{statusNameListMap[row.status].icon}
{statusNameListMap[row.status].label}
</t-tag>
);
},
},
// title 对应有图标的表头
{
colKey: 'applicant',
title: 'title-slot-name',
width: 120,
},
// render 对应申请时间
{
colKey: 'createTime',
// render 可以渲染表头,也可以渲染单元格。但 title 只能渲染表头,cell 只能渲染单元格
render(h, context) {
const { type, row, col } = context;
return {
title: '申请时间',
cell: row && row[col.colKey],
}[type];
},
},
然后以下是我的使用,我的实现结果,贴图:
表格结构定义:
colums: 表头 定义字段,宽度,行合并/列合并,表头对应该列的样式
table:定义表格数据,其中字段对应表头字段,然后可以设置行、列合并(非自带),传给下面函数处理
tableList:由于没设标识,所以给数据增加index作为标识,不然会报错
rowspanAndColspan:函数,设置单元格所占的行,列的格子数,会对满足条件的单元格使用
attrs设置了样式,cell,title绑定了插槽
// 表头 定义字段,宽度,行合并/列合并,表头对应该列的样式
const columns = [
{
colKey: 'compare',
title: '测试1',
width: '200px',
colspan: 2,
attrs: () => {
return {
style: {
fontWeight: 600,
},
}
},
},
{
colKey: 'content',
title: '测试2',
width: '279px',
align: 'center',
},
{
colKey: '测试3',
title: 'title-vip',
width: '239px',
align: 'center',
cell: 'vip',
attrs: () => {
return {
style: {
backgroundColor: 'rgba(239, 158, 0, 0.1)',
border: '1px solid #FFD583',
},
}
},
},
{
colKey: 'normal',
title: '测试4',
width: '239px',
align: 'center',
cell: 'normal',
},
{
colKey: 'other',
title: '测试5',
width: '239px',
align: 'center',
cell: 'other',
},
]
// 定义表格数据,其中字段对应表头字段,然后可以设置行、列合并,这里主要是传给下面函数处理
const table = [{
compare: '项目',
content: '项目',
vip: true,
normal: 'test',
other: false,
colspan: 2,
}, {
compare: '项目',
vip: true,
normal: 'test',
other: '其他',
colspan: 2,
}, {
compare: '项目',
content: '项目',
vip: true,
normal: true,
other: false,
rowspan: 4,
}, {
compare: '项目',
content: '项目',
vip: true,
normal: true,
other: false,
}, {
compare: '项目',
content: '项目',
vip: true,
normal: true,
other: false,
}, {
compare: '项目',
content: '企业',
vip: true,
normal: true,
other: false,
}, {
compare: '项目测试',
content: '',
vip: true,
normal: true,
other: '其他',
colspan: 2,
}, {
compare: '下载',
content: '',
vip: true,
normal: '其他',
other: '其他',
colspan: 2,
}, {
compare: '项目查看 ',
content: '',
vip: true,
normal: '其他',
other: '其他',
colspan: 2,
}, {
compare: '项目查看',
content: '项目1',
vip: true,
normal: '其他',
other: '其他',
rowspan: 4,
}, {
compare: '项目查看',
content: '项目2',
vip: true,
normal: true,
other: false,
}, {
compare: '项目查看',
content: '项目3',
vip: true,
normal: true,
other: false,
}, {
compare: '项目查看',
content: '项目4',
vip: '测试数据',
normal: '其他',
other: '其他',
}]
// 给数据增加index作为标识,不然会报错
const tableList = table.map((item, index) => ({
...item,
index,
}))
// 这个是设置单元格所占的行,列的格子数
function rowspanAndColspan({ row, colIndex }) {
if (colIndex === 0) {
const colspan = row.colspan || 1
const rowspan = row.rowspan || 1
return {
colspan,
rowspan,
}
}
}
表格:
<div>
<t-table
:bordered="true"
:data="tableList"
:columns="columns"
row-key="index"
:rowspan-and-colspan="rowspanAndColspan"
:resizable="false"
lazy-load
>
<template #title-vip>
<div class="cell-style font-600">
<img src="../../assets/img/vip/icon-vip.png"
alt="" class="vipimg cell-img mr-1"> 测试3
</div>
</template>
<template #vip="{ row }">
<div v-if="row.vip === true" class="cell-style">
<div class="true cell-img">
<img src="../../assets/img/vip/icon-true.png" alt="">
</div>
</div>
<div v-else>
{{ row.vip }}
</div>
</template>
<template #normal="{ row }">
<div v-if="row.normal === true" class="cell-style">
<div class="true cell-img">
<img src="../../assets/img/vip/icon-true.png" alt="">
</div>
</div>
<div v-else>
{{ row.normal }}
</div>
</template>
<template #other="{ row }">
<div v-if="row.other === false" class="cell-style">
<div class="false cell-img">
<img src="../../assets/img/vip/icon-false.png" alt="">
</div>
</div>
<div v-else>
{{ row.other }}
</div>
</template>
</t-table>
</div>
踩坑1:
设置headerAffixedTop的吸顶,会因为表头合并而出现滚动条的问题,升级版本也没办法
解决方法:不能完全解决,只能采取相似效果实现
将表头的colspan:2的合并给去掉,然后把后边的第一个格子设置className,定义样式,左边框去掉
踩坑2:
表头的样式定义:无直接设置表头的样式
1. 使用className设置实现,但是需要设置每一个
2. 使用样式穿透
:deep(.t-table__th-cell-inner) {
font-weight: 600;
color: black;
}