前言:S2 的透视表提供了 3 种基本的布局方案,自动/紧凑/列等宽,基本可以覆盖大多数的使用场景,但是现有项目中的业务场景比较复杂,会对单元格内的数据进行定制化处理,导致单元格内的数据文本会很长,导致数据省略显示,需要自定义实现方案,确保在紧凑模式下可以看到全量的数据展示。
问题定位及实现方案推进:
紧凑模式下如何实现定制列宽?
在透视图的配置项中有一个s2Options/style/colCell,这个配置项中
官方给出了 2 种方案:
方案1是更改colCell中的 width
方案2 是更改 colCell 中的widthByField
但是 width 有个问题点,透视表支持用户手动拖拽列,然后拖拽的时候会重新计算列宽,导致 width 的配置失效,但是widthByField不会,所以针对widthByField设计解决方案。
widthByField是怎么生效的?该怎么配置
经过详细比对生成的数据和官方 demo 中的代码案例,发现如果要精确控制列头的宽度,只能采用 id + width 的方案,id 是 叶子节点的 id,然后只要能获取到想要控制的叶子节点的 id 以及要展示的宽度,就能实现相应的效果!
// 官方 demo
const s2Options = {
style: {
rowCell: {
widthByField: {
// 调整具体单元格
'root[&]浙江省[&]杭州市': 60,
},
},
},
}
怎么获取到列头叶子节点以及对应的Id?
在使用透视表的时候需要创建一个 SheetComponent 的容器,然后绑定一个 ref,大致结构如下
import { SheetComponent } from '@antv/s2-react'
const sheetRef = useRef()
...
<SheetComponent
ref={sheetRef}
></SheetComponent>
然后可以通过sheetRef.current.facet.getColLeafNodes()获取到所有的叶子节点,

好了,下一步就是拿到对应这一列的宽度,不就问题就搞定了嘛!胜利就在眼前!但是对应列宽度怎么算?抓耳挠腮.gif,突然意识到透视表是需要用户手动传数据以及行列配置的

举个🌰,接下来我们使用官方 demo 中提供的数据进行解读:
// 配置项 fields ⭐️⭐️⭐️⭐️⭐️ 超级重要!下边会用到!!!!
{
rows: ["province","city"],
columns: ["type","sub_type"],
values: ["number"],
valueInCols: true
}
// 配置项 meta
[
{"field": "number","name": "数量"},
{"field": "province","name": "省份"},
{"field": "city","name": "城市"},
{"field": "type","name": "类别"},
{"field": "sub_type","name": "子类别"}
]
// 数据 data
[
{
"number": 7789,
"province": "浙江省",
"city": "杭州市",
"type": "家具",
"sub_type": "桌子"
},
{
"number": 2367,
"province": "浙江省",
"city": "绍兴市",
"type": "家具",
"sub_type": "桌子"
},
...
]
最终的页面

上面这一堆就是透视表的核心配置以及渲染出来的效果,然后我们的项目当中在处理配置项 meta 的时候单独加了 formatter【透视表提供的能力】去处理数据,然后我误以为这边就是获取的实时数据,然后在这边进行数据获取,最终发现大错特错,这个 formatter 函数就是一个单纯的处理函数,而且只会在表格渲染的时候调用,而且只能拿到可视区域的数据,如果数据量较大,并且溢出可视区域的话,是拿不到所有数据的。
怎么办?没招了!?
思来想去总感觉方向是错的,最后复盘的时候发现其实逻辑上是有问题的。想要实现的效果是在表格渲染之前获取到最终渲染出来的表格列头数据,拿到对应的 id,然后把表格数据分组计算最宽单元格,但是实际情况是最新的列头数据只有在表格的渲染完成之后才能拿到,我们还需要进行二次渲染,总归是有各种问题。那可不可以在渲染之前就拿到最终渲染的列头数据呢?然后发现,最终通过sheetRef.current.facet.getColLeafNodes()获取到的叶子节点数据我们是可以通过fields和 meta 构建出来的!!
用这张图举例,如果最终渲染效果是这样,那么表格最终渲染出来之后的列头数据格式是
// 第一个数量
{
id:'root[&]家居[&]桌子[&]数量',
...其他属性
},
//第二个数量
{
id:'root[&]家居[&]沙发[&]数量',
...其他属性
},
// 家居下的桌子单元格的 id 是
{
id:'root[&]家居[&]桌子'
}

说到这儿有的朋友可能就已经猜到怎么拼了,列头的层级其实就是根据fields中的 columns 依次分层,然后以笛卡儿积的形式创建一个新的数据集,并且手动拼接对应的 id 即可,然后 values 中的数据就是最后一层那个数量,如果valueInCols是 true,就代表数量是在列头上,如果是 false,就在左边的行内,其实很简单。然后手搓一个函数实现这个逻辑即可,然后在函数内部,可以通过 data 进行递归遍历找到符合条件的数据,然后进行分组即可,s2 表格最终生成的表头数据中会携带一个 query 属性,记录的是当前单元格的维度信息,例如第一个“数量”单元格的 query 属性就是
{
type:"家居",
subType:"桌子",
value:"数量",
}
其他单元格的生成逻辑和这个保持一致
我是根据这个 query,在手动生成列头数据的时候就把这个 query 也动态拼接,然后根据这个 query 找到对应的数据进行分组,然后找到每个分组中字符串最宽的一个数据,最终生成一个关于列 id 和宽度的对象即可,类似于
const customWidthByField = {
'root[&]家居[&]桌子[&]数量':120,
'root[&]家居[&]沙发[&]数量':150,
'root[&]办公用品[&]笔[&]数量':200,
...
}
最后在s2Option 中塞进去即可
{
style:{
colCell:{
widthByField: {
...customWidthByField,
}
}
}
}
代码由于保密等问题,不方便展示,核心思路就是根据 fields 中的 columns/values/valueInCols就可以知道最终的列头结构是什么,然后可以根据结构手动拼接出对应的 id,然后就可以继续后续的业务操作。

2192

被折叠的 条评论
为什么被折叠?



