背景
SpreadJS 是一款企业级纯前端表格控件,不仅完整兼容 Excel 的核心功能,更提供了强大的深度定制与二次开发能力。在实际项目中,用户往往不满足于原生功能,需要将表格与其他前端生态无缝整合。
为什么需要集成第三方图表?
- 原生图表局限:SpreadJS 虽兼容 Excel 图表,但复杂可视化场景(如动态交互、自定义样式、特殊图表类型)需要更灵活的方案。
- 生态整合需求:前端项目中已广泛使用 ECharts、AntV 等图表库,重复造轮子成本高。
- 定制化场景:金融看板、量化报表、数据大屏等场景需要表格与高级图表深度联动。
SpreadJS 的扩展优势:
- 浮动对象机制:支持嵌入任意 DOM 内容,为第三方控件提供容器。
- 事件系统完善 :
ValueChanged、TopRowChanged等事件支持精细化的交互控制。 - API 开放度高:从数据绑定到导出渲染,各环节均可自定义扩展。
本文以 ECharts 为例,演示如何将主流图表库与 SpreadJS 深度集成,实现数据联动、可见性同步及 PDF 导出。该方案同样适用于其他 DOM 依赖型控件,展现 SpreadJS 作为前端表格基座的生态整合能力。
核心方案:容器嵌套
利用 SpreadJS 的 FloatingObject 支持嵌入 HTML 内容的特性,为 ECharts 提供 DOM 容器。
1. 创建浮动容器
通过 FloatingObject.content() 将 div 插入表格指定单元格区域。
JavaScript
function initFloatingObject(sheet, chart) {
let floatObj = new GC.Spread.Sheets.FloatingObjects.FloatingObject(chart.id);
floatObj.startRow(chart.startRow);
floatObj.endRow(chart.endRow);
floatObj.startColumn(chart.startColumn);
floatObj.endColumn(chart.endColumn);
let div = document.createElement('div');
div.innerHTML = `<div id="${chart.id}" style="width:100%;height:100%;"></div>`;
floatObj.content(div);
sheet.floatingObjects.add(floatObj);
}

2. 初始化 ECharts
容器挂载后,实例化图表。
JavaScript
function initBarECharts(chart) {
let dom = document.getElementById(chart.id);
if (!dom) return;
let myChart = echarts.init(dom);
let data = getChartDataFromTables(chart.source);
myChart.setOption({
xAxis: { data: data.categories },
series: [{
type: 'bar',
data: data.data,
animation: true
}]
});
return myChart;
}

数据双向绑定
利用 ValueChanged 事件监听单元格修改,实现表格驱动图表更新。
实现逻辑
- 监听全局
ValueChanged事件。 - 校验修改位置是否命中图表数据源区域。
- 命中则提取新数据,调用
chart.setOption刷新。
JavaScript
spread.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) {
for (let key in charts) {
let range = new GC.Spread.Sheets.Range(
charts[key].table.row, charts[key].table.col,
charts[key].table.rowCount, charts[key].table.colCount
);
// 判断修改是否在当前图表数据范围内
if (range.contains(info.row, info.col, 1, 1)) {
refreshCharts(charts[key].id, getChartDataFromTables(charts[key].source));
break;
}
}
});
refreshCharts方法是用于动态设置ECharts数据的方法,这里就展示详细的内部实现了。 
可见性同步:模拟原生滚动渲染
SpreadJS 原生图表具备可视区域自动渲染机制(滚动到可见区域才渲染),但嵌入的自定义 DOM 内容不会自动触发此逻辑。若不处理,滚动到图表位置时会发现内容为空。
监听滚动事件
需手动监听 TopRowChanged 事件,当图表行进入可视区附近时,触发初始化。
JavaScript
spread.bind(GC.Spread.Sheets.Events.TopRowChanged, function (s, e) {
let newTopRow = e.newTopRow;
// 判断图表起始行是否进入可视区,且尚未初始化
if ((charts["line"].startRow - 5 < newTopRow) && (!charts["line"].echart)) {
initCharts(charts["line"]);
}
});

难点突破:导出 PDF
ECharts 的 DOM 节点无法被 SpreadJS 原生 PDF 导出功能捕获。解决方案:临时 Workbook + 图片转换。
导出流程
- 克隆 Workbook:深拷贝当前 JSON 配置到临时实例,隔离操作。
- 强制渲染:确保临时实例中图表已初始化。
- 转图片 :调用
echarts.getDataURL()获取 Base64。 - 替换对象:移除 FloatingObject,在原位置添加 Picture Shape。
- 生成 PDF :调用临时实例的
savePDF。 1.
JavaScript
document.getElementById("saveAsPdf").addEventListener("click", function () {
// 1. 克隆临时实例
let tempSpread = new GC.Spread.Sheets.Workbook();
tempSpread.fromJSON(JSON.parse(JSON.stringify(spread.toJSON({ includeBindingSource: true }))));
let tempSheet = tempSpread.getSheet(0);
// 2. 图表转图片
for (let key in charts) {
// 确保图表已渲染
if (!charts[key].echart) {
// 触发初始化逻辑...
}
let imgData = charts[key].echart.getDataURL();
tempSheet.floatingObjects.remove(charts[key].id); // 移除 DOM 容器
// 添加图片形状
let pic = tempSheet.shapes.addPictureShape(charts[key].id, imgData, 0, 0, 100, 100);
pic.startRow(charts[key].startRow);
pic.endRow(charts[key].endRow);
}
// 3. 导出
tempSpread.savePDF(function (blob) {
saveAs(blob, 'report.pdf');
});
});

总结
通过本文的实践,我们验证了 SpreadJS 在深度定制与生态整合方面的强大能力:
| 集成维度 | 实现方式 | 扩展价值 |
|---|---|---|
| 容器嵌入 | FloatingObject.content() 嵌入 DOM | 可集成任意前端控件(图表、地图、富文本等) |
| 数据联动 | ValueChanged 事件监听 | 实现表格与外部组件的双向数据同步 |
| 渲染同步 | TopRowChanged 事件模拟原生逻辑 | 保持与 SpreadJS 原生组件一致的用户体验 |
| 导出扩展 | 临时 Workbook + 图片转换 | 突破原生导出限制,支持自定义内容输出 |
核心价值:
- 不重复造轮子:直接复用 ECharts 等成熟图表库,降低开发成本。
- 保持体验一致:通过事件监听模拟原生行为,用户无感知切换。
- 方案可迁移:该集成模式适用于其他 DOM 依赖型第三方控件。
SpreadJS 不仅是一个表格控件,更是一个可扩展的前端数据交互基座。通过开放的事件系统与 API,开发者可以将表格与现有前端生态无缝融合,快速构建满足复杂业务需求的在线报表系统。
完整demo请查看:在SpreadJS中集成ECharts并导出为PDF