前言
由于我们业务中有很多地方需要有纵向复制,刷选等操作,一般的传统表格并不支持。
目前支持度较好的葡萄城表格与handsontable都需要收费,而s2作为一款轻量级开源表单较为符合我们的需求,但是由于s2仍然不够成熟,有非常多的功能需要自定义。
我在公司对s2做了大量封装,对里面代码基本上算比较熟悉了。本篇结合我们业务中的需求,带大家看看s2可以做到什么地步,需要进行怎样的改动。
引入方式与注意点
传统引入方式就是npm与umd的这里就不说了,值得说一下的就是模块联邦引入。
正常情况使用模块联邦打包share出去
需要引入的项目里同样需要配上share,否则全局变量部分就会有问题,如果s2的表格的hover效果刷选效果全都消失可能是这个原因导致的(我们遇到2个项目出现这问题,加上就好)。
s2前置知识
官网:https://s2.antv.antgroup.com/
对于完全没接触过s2的同学来说,有些概念需要先了解下,便于后面更快地对s2进行改造。
s2表格从大类上分明细表与透视表,其他表都是2个表的延伸:
透视表比明细表多了角头与行头,它只有列头与数据。
一般业务需求基本都是明细表,只有少部分需求会用到多维表。多维表的行头可以转换成树结构,可以做要求较低的树状表格。
s2是基于图引擎g做的,目前2版本使用的是4版本的g:https://g.antv.vision/zh/docs/guide/introduce
如果需要对单元格等进行自定义,一般会需要使用g进行绘制。
s2的自定义扩展基本都是需要继承之前的类。比如列头就要继承colCell
,单元格就要继承DataCell
。所以你需要改什么就去继承重写就可以了。
比如下面这个例子就是全部改写单元格(datacell)的效果:
主题修改
我觉得官网文档上主题修改讲的挺详细的了,就是修改themeCfg
。
默认色板可以通过getPalette
获取,如果只想改主题色,换掉brandColor
即可。
const palette = getPalette("default");
const color = generatePalette({
...palette,
brandColor: "#ff6f00",
});
直接看官网这部分即可https://s2.antv.antgroup.com/zh/examples/theme/custom/#custom-manual-palette
由于s2-react使用了antd4做组件,所以当你改了颜色后,antd的主题色最好也需要更改,否则会比较突兀:
@import "~antd/es/style/themes/default.less";
@import "~antd/dist/antd.less";
@primary-color: #ff6f00; // 全局主色
@link-color: #ff6f00; // 链接色
导入less换变量即可。如果编译报错,可以开启lessOption:
{
loader: "less-loader",
options: {
lessOptions: {
javascriptEnabled: true,
},
},
},
svgIcon
s2的svg引入稍微有点坑,所以这里抽出来写一下。
一般我们使用的icon需要注册到headerActionIcons
中进行使用。s2会将你配置的图标以及名称放到全局变量上,等需要的时候拿取。
在列头行头等地方需要拿取icon时,s2有个icon类会去修改fill(如果使用网络图片就不会受影响),具体代码是这么写的:
/**
* 使用 iconfont 上的 svg 来创建 Icon
*/
export class GuiIcon extends Group {
static type = '__GUI_ICON__';
// icon 对应的 GImage 对象
public iconImageShape: Shape.Image;
constructor(cfg: GuiIconCfg) {
super(cfg);
this.render();
}
// 获取 Image 实例,使用缓存,以避免滚动时因重复的 new Image() 耗时导致的闪烁问题
/* 异步获取 image 实例 */
public getImage(
name: string,
cacheKey: string,
fill?: string,
): Promise<HTMLImageElement> {
return new Promise<HTMLImageElement>((resolve, reject): void => {
let svg = getIcon(name);
if (!svg) {
return;
}
const img = new Image();
img.onload = () => {
ImageCache[cacheKey] = img;
resolve(img);
};
img.onerror = reject;
// 兼容三种情况
// 1、base 64
// 2、svg本地文件(兼容老方式,可以改颜色)
// 3、线上支持的图片地址
if (svg.includes(SVG_CONTENT_TYPE) || this.isOnlineLink(svg)) {
img.src = svg;
} else {
// 传入 svg 字符串(支持颜色fill)
if (fill) {
// 如果有fill,移除原来的 fill
// 这里有一个潜在的问题,不同的svg里面的schema不尽相同,导致这个正则考虑不全
// 1、fill='' 2、fill 3、fill-***(不需要处理)
// eslint-disable-next-line no-useless-escape
svg = svg.replace(/fill=[\'\"]#?\w+[\'\"]/g, ''); // 移除 fill="red|#fff"
svg = svg.replace(/fill>/g, '>'); // fill> 替换为 >
}
svg = svg.replace(
STYLE_PLACEHOLDER,
`${
STYLE_PLACEHOLDER} fill="${
fill}"`,
);
// 兼容 Firefox: https://github.com/antvis/S2/issues/1571 https://stackoverflow.com/questions/30733607/svg-data-image-not-working-as-a-background-image-in-a-pseudo-element/30733736#30733736
// https://www.chromestatus.com/features/5656049583390720
img.src = `${
SVG_CONTENT_TYPE};utf-8,${
encodeURIComponent(svg)}`;
}
});
}
/**
* 1. https://xxx.svg
* 2. http://xxx.svg
* 3. //xxx.svg
*/
public isOnlineLink = (src: string) => {
return /^(https?:)?(\/\/)/.test(src);
};
private render() {
const {
name, fill } = this.cfg;
const attrs = clone(this.cfg);
const imageShapeAttrs: ShapeAttrs = {
...omit(attrs, 'fill'),
type: GuiIcon.type,