🙋🏻♀️ 编者按:产品突然指过来一个需求,需要在 S2 大数据的表格里面,添加可编辑功能。这可咋整呀?S2 的主打的是数据展示,而不是数据编辑,看来只能自己试试黑魔法了😏。本文中,AntV 团队的亚德将为大家演示,如何把 S2 数据展示表格变成编辑表格,欢迎享用~
import React, { useState, useRef } from 'react';
import ReactDOM from 'react-dom';
import { SheetComponent } from '@antv/s2-react';
import '@antv/s2-react/dist/style.min.css';
import 'antd/es/checkbox/style/index.css';
import { S2DataConfig, S2Options, SpreadSheet } from '@antv/s2';
const initOptions = {
width: 600,
height: 400,
showSeriesNumber: true,
tooltip: { showTooltip: false },
interaction: { enableCopy: true, },
showDefaultHeaderActionIcon: false,
}
const initData = {
fields: { columns: ['province', 'city', 'type', 'price'], },
sortParams: [],
}
const App = ({ data }) => {
const S2Ref = useRef<SpreadSheet>(null);
const [options, setOptions] = useState<S2Options>(initOptions);
const [dataCfg, setDataCfg] = useState<S2DataConfig>({ ...initData, data });
return (
<div>
<SheetComponent
ref={S2Ref}
dataCfg={dataCfg}
options={options}
sheetType="table"
/>
</div>
);
};
fetch('../data/basic-table-mode.json')
.then((res) => res.json())
.then((res) => {
ReactDOM.render(<App data={res} />, document.getElementById('container'));
});
1. 先来一个受控的编辑元素
<input value={value} onChange={e => setValue(e.target.value)} />
2. 注册一个 DATA_CELL_CLICK 事件,触发后保存当前点击 Cell 的信息
S2Ref.current.on(S2Event.DATA_CELL_CLICK, (e) => {
// 保存当前cell
setCell(e.target.cfg.parent)
})
3. 通过当前 Cell 信息将受控编辑元素覆盖到当前 Cell 上方,同时赋初始值
useEffect(() => {
const spreadsheet = S2Ref.current
if (spreadsheet && cell) {
const cellMeta = pick(cell.getMeta(), ['x', 'y', 'width', 'height', 'fieldValue']);
const colCellHeight = (spreadsheet.getColumnNodes()[0] || { height: 0 }).height
cellMeta.x -= scroll.scrollX || 0;
cellMeta.y -= (scroll.scrollY || 0) - colCellHeight;
setPosition({
left: cellMeta.x,
top: cellMeta.y,
width: cellMeta.width,
height: cellMeta.height,
})
setShow(true)
setValue(cellMeta.fieldValue)
}
}, [cell])
// show的时候自动focus
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus({ preventScroll: true })
}
}, [show])
4. 使用回车键触发保存功能
// 改变S2实际渲染内容
const onSave = (inputVal: string) => {
const spreadsheet = S2Ref.current
if (spreadsheet && cell) {
const { rowIndex, valueField } = cell.getMeta();
spreadsheet.dataSet.originData[rowIndex][valueField] = inputVal;
spreadsheet.render(true);
setShow(false)
}
}
// 绑定回车键 回车的时候触发保存
useEffect(() => {
const onKeyDown = (e) => {
if (e.keyCode === 13) {
e.preventDefault();
onSave(value);
}
};
if (inputRef.current) {
inputRef.current.addEventListener('keydown', onKeyDown)
}
return () => {
inputRef.current?.removeEventListener('keydown', onKeyDown)
}
}, [value])
5. 切换 Cell 之前,先保存当前编辑的值
// 绑定 S2Event.DATA_CELL_CLICK 事件,触发时先将上一个cell的值保存,然后设置当前cell
useEffect(() => {
const spreadsheet = S2Ref.current
const handleClick = (e) => {
onSave(value)
setCell(e.target.cfg.parent)
}
if (spreadsheet) {
spreadsheet.on(S2Event.DATA_CELL_CLICK, handleClick)
}
return () => {
spreadsheet?.off(S2Event.DATA_CELL_CLICK, handleClick)
}
}, [value])
6. 监听滚动事件,使得编辑元素动态滚动
useEffect(() => {
const spreadsheet = S2Ref.current
const handleScroll = (e) => {
if (spreadsheet) {
const newScroll = spreadsheet.facet.getScrollOffset()
if (!isEqual(newScroll, scroll)) {
setScroll(spreadsheet.facet.getScrollOffset())
}
}
}
if (spreadsheet) {
spreadsheet.on(S2Event.LAYOUT_CELL_SCROLL, handleScroll)
}
return () => {
spreadsheet?.off(S2Event.LAYOUT_CELL_SCROLL, handleScroll)
}
}, [])
// 监听滚动
useEffect(() => {
const spreadsheet = S2Ref.current
if (spreadsheet && cell) {
const cellMeta = pick(cell.getMeta(), ['x', 'y', 'width', 'height', 'fieldValue']);
cellMeta.x -= scroll.scrollX || 0;
cellMeta.y -=
(scroll.scrollY || 0) -
(spreadsheet.getColumnNodes()[0] || { height: 0 }).height;
setPosition({
left: cellMeta.x,
top: cellMeta.y,
width: cellMeta.width,
height: cellMeta.height,
})
}
}, [scroll])