前言
Microsoft Excel工作表导航功能能够提取工作簿内每个工作表中的表格(Table)、数据透视表(PivotTable)、图表(Chart)、图片、形状(Shape)等元素,并将其摘要信息以树的形式罗列显示,当点击某元素信息时,工作簿跳转至该元素所在工作表,并聚焦在该元素上,如图1所示。
(图1. Excel 工作表导航)
那么SpreadJS是否支持自定义开发实现该功能呢?撰写本文的目的正是为了介绍在SpreadJS中如何利用现有功能或产品设计自定义开发实现该功能,因此答案显然是“支持”。接下来,我们一起走进工作表导航自定义实现方案。
我们来分析下工作表导航功能的要素,参考Microsoft Excel官网对该功能的介绍,包含以下需求项:
- 罗列工作簿内每个工作表中的元素。
- 点击某元素可以跳转至相关工作表并聚焦该元素。
- 导航面板支持模糊搜索匹配工作表元素。
首先,需要确定SpreadJS是否能够获取工作表中的上述元素信息。SpreadJS对上述元素均抽象了独立的管理器,比如TableManager管理Table实例,PivotTableManager管理PivotTable实例,ChartCollection管理Chart实例、ShapeCollection管理Shape实例,如图2所示。由于17.0.0版本优化了图表Chart实现,图表不再是基于DOM元素的浮动对象,而是作为一种形状存在,所以图表Chart实例也可由ShapeCollection管理。以上,便可确定,通过各类型元素的管理器便可获取工作表中对应类型的各元素。
(图2. 工作表元素管理)
其次,各类型元素的管理器可以以元素名称为检索依据快速获取元素实例。以Table为例,TableManager:findByName()根据Table名称获取Table实例,Table实例中包含了Table所在单元格区域,Worksheet:setSelection()便可选中区域,聚焦该区域。PivotTable同理,PivotTable整体布局在工作表中,占据一片连续的单元格区域,通过PivotTable:getRange()即可得到数据透视表所在单元格区域。形状Shape实体悬浮在工作表图层上一层,并不占据单元格,便无法通过Worksheet:setSelection()聚焦。然而,ShapeBase:isSelected()可聚焦选中调用者形状实例
至此,SpreadJS便提供了完整的工作表元素管理能力,凭借此能力可以增删和访问元素。如何以树的形式布局这些元素,呈现一个清晰直观的树结构,以供用户快速了解工作表以及工作簿元素组成全貌呢?此外,如何同时提供交互能力呢?
我们来看一下Microsoft Excel中工作表导航的布局结构,如图3所示,工作表视图区域右侧显示一设置面板,面板内自上而下分为两部分,包括搜索输入栏和工作表元素信息列表树。
(图3. 工作表导航面板)
我们知道SpreadJS中的各个设置面板均为IDialogTemplate实例,且SpreadJS公开了IDialogTemplate结构标准以支持自定义扩展。在此,我们可以参考AtmoicComponentBase标准自定义原子组件,这便是工作表元素列表树的布局容器,在容器内自上而下依次创建DOM元素封装每一级树节点,以形成工作表树元素列表树。树包括两级节点,父节点显示工作表名称,代表一个工作表实例;相应地,子节点即工作表内的诸多元素名称。在定义工作表元素布局的同时,定义搜索框的模糊搜索逻辑,展开父节点以显示符合搜索关键字的匹配项,当键入或删除新字符后导致当前匹配项中部分项不再匹配时,隐藏相关项并折叠无匹配项的父节点。
工作表导航面板既已实现,再考虑该面板显示与否的控制要求。Microsoft Excel的工作表导航功能位于工具栏–>视图–>显示–>导航,参考此路径,在工具栏–>视图选项卡中新增自定义功能按钮,设置图标以诉诸该图标为工作表导航功能按钮,如图4所示。
(图4. 工作表导航按钮)
对该自定义功能按钮定义相应的命令,在该按钮被点击时触发,通过切换工作表导航面板对应的命令的visibleContext属性值控制面板可见性。
至此,便利用SpreadJS的元素管理能力自定义实现了与Microsoft Excel相似的工作表导航功能,如图5所示。附上demo,以供参考。
(图5. SpreadJS 工作表导航)
工作表导航demo(Html)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>工作表导航</title>
<meta name="spreadjs culture" content="zh-cn"/>
<link href="http://cdn.grapecity.com/spreadjs/hosted/css/gc.spread.sheets.excel2013white.18.1.0.css"
rel="stylesheet" type="text/css"/>
<link href="http://cdn.grapecity.com/spreadjs/hosted/css/gc.spread.sheets.designer.18.1.0.min.css" rel="stylesheet"
type="text/css"/>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/gc.spread.sheets.all.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.shapes.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.charts.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.slicers.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.print.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.barcode.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.pdf.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.pivot.pivottables.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.tablesheet.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.ganttesheet.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.report.reportsheet.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.formulapanel.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/plugins/gc.spread.sheets.io.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/interop/gc.spread.excelio.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/resources/zh/gc.spread.sheets.resources.zh.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/gc.spread.sheets.designer.resource.cn.18.1.0.min.js"></script>
<script src="http://cdn.grapecity.com/spreadjs/hosted/scripts/gc.spread.sheets.designer.all.18.1.0.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
<style>
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
body {
position: absolute;
margin: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.gc-across-sheet-ftb {
z-index: -1 !important;
}
.gc-across-sheet-func-popup {
z-index: -1 !important;
}
.sheet-name-container {
width: 80%;
height: 50px;
display: block;
margin-top: 5px;
background-color: white;
}
.tree-container {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
padding: 16px;
margin-top: 24px;
}
.tree-node {
margin-bottom: 8px;
border-radius: 4px;
overflow: hidden;
}
.node-header {
display: flex;
align-items: center;
padding: 10px 12px;
background-color: #f1f5f9;
cursor: pointer;
transition: background-color 0.2s;
}
.node-header:hover {
background-color: #e2e8f0;
}
.toggle-btn {
width: 24px;
height: 24px;
border-radius: 50%;
background-color: #dbeafe;
color: #3b82f6;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
transition: transform 0.3s;
border: none;
cursor: pointer;
}
.node-header.expanded .toggle-btn {
transform: rotate(45deg);
}
.node-name {
font-weight: 500;
}
.node-children {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
padding-left: 36px;
position: relative;
}
.node-children::before {
content: '';
position: absolute;
left: 12px;
top: 0;
bottom: 0;
width: 2px;
background-color: #e2e8f0;
}
.child-item {
padding: 8px 12px;
margin: 4px 0;
background-color: #f8fafc;
border-radius: 4px;
transition: background-color 0.2s;
}
.child-item:hover {
background-color: #f1f5f9;
}
.child-item.selected {
background-color: #e0f2fe;
border-left: 3px solid #3b82f6;
}
.empty-message {
padding: 8px 12px;
color: #64748b;
font-style: italic;
}
</style>
</head>
<body>
<div class="sample-tutorial">
<div class="demo-options">
<div class="option-row">
<div id="formulaBar" contenteditable="true" spellcheck="false"
style="font-family: Calibri;border: 1px solid #808080;width:100%;box-sizing: border-box;"></div>
</div>
</div>
<div id="ss" style="width:100%; height: calc(100% - 20px)"></div>
</div>
</body>
<script>
window.onload = function () {
var designer = new GC.Spread.Sheets.Designer.Designer(document.getElementById('ss'));
var spread = designer.getWorkbook();
spread.setSheetCount(4);
initSpread(spread);
initNavigator(designer);
};
function initSpread(spread) {
var sheet1 = spread.getSheet(0);
sheet1.setArray(0, 0, [
['名称', '数值'],
['A', 1],
['B', 2],
['C', 3],
['D', 4]
]);
// 数据透视表
var pivotTable = sheet1.pivotTables.add('pivottable1', '=Sheet1!A1:B3', 5, 0, GC.Spread.Pivot.PivotTableLayoutType.outline, GC.Spread.Pivot.PivotTableThemes.medium2);
pivotTable.add('名称', '名称', GC.Spread.Pivot.PivotTableFieldType.rowField);
pivotTable.add('数值', '数值', GC.Spread.Pivot.PivotTableFieldType.valueField, GC.Pivot.SubtotalType.sum);
var sheet2 = spread.getSheet(1);
sheet2.setArray(0, 0, [
['属性1', '属性2', '属性3', '属性4'],
[1, 2, 3, 4],
[2, 3, 4, 5]
]);
// 表格Table
sheet2.tables.add('table', 0, 0, 3, 4);
sheet2.setArray(7, 0, [
['属性1', '属性2', '属性3', '属性4'],
[1, 2, 3, 4],
[2, 3, 4, 5]
]);
// 表格Table
sheet2.tables.add('table2', 7, 0, 4, 4);
var sheet3 = spread.getSheet(2);
sheet3.setArray(0, 0, [
[1, 2, 3, 4],
[1, 2, 3, 4]
]);
// 区域快照
var cameraShape = sheet3.shapes.addCameraShape('CameraShape', 'Sheet3!A1:D2', 240, 200);
// 图片
var imgShape = sheet3.shapes.addPictureShape('pic1', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAWtJREFUSEvt1r1KHWEQxvGfH50Saz+iuYWINyGx00sIplAD6QzpYikWohC0tLcSEu9BorcgMailiqQy6Bl4V45HX9w9RzzNvu3OzDPPf2f23R5dOj1d0lULvxr5GnWB+gO2MNoh+7+Yx6/WOjnUJxjrULRIj1rjZYVvU+AQrtpsYADXKfeRwZzjS7xpYDrFZ+xWFJ/BJibwD9HEg5MTHsE65lL0Hhbx55kGYiYibzbF/cQCjssKNw9ZdP4udf4da7hpKdSLjw1nq4nUGZaxk2s05/gHvuIiYQrBQN6PwzSpv1PRybQBU/iPyP2WZmMQK/hS1nEM13mj4HBTwntsoxAIEhG31CjehyN8wkFTTryatzy+E3KOi6lufd6KNDRieHKvIFcneztlE5Kb2PGN5CQcx64+dV5cuOx21cL3pKoOV1nERVxl1F27JKbTznZ6Q4WBuBb3y35AqiKtHF//+lRG1m5CjbpdcpXz7gDWz0QfNVlZrQAAAABJRU5ErkJggg==', 100, 100, 200, 200);
// 普通形状
var baseShape = sheet3.shapes.add("heartShape", GC.Spread.Sheets.Shapes.AutoShapeType.heart, 100, 50, 100, 150);
var sheet4 = spread.getSheet(3);
sheet4.setArray(0, 0, [
['名称', '数值'],
['A', 1],
['B', 2],
['C', 3],
['D', 4]
]);
// 图表
var chart = sheet4.charts.add('名称-数值-柱状图', GC.Spread.Sheets.Charts.ChartType.columnClustered, 0, 100, 400, 300, 'A1:B5');
}
/**
* 初始化sheet导航功能及其相关元素(工具栏功能按钮、导航面板)
*/
function initNavigator(designer) {
var config = GC.Spread.Sheets.Designer.DefaultConfig;
GC.Spread.Sheets.Designer.Designer.RegisterComponent('MyComponent', MyTemplateComponent);
// 定义命令控制sheet导航面板显示与否
config.commandMap = {
SheetNaviCmd: {
title: 'SheetNavi',
text: 'sheet导航',
iconClass: "ribbon-button-zoom",
bigButton: 'false',
commandName: 'SheetNavi',
execute: async (context, propertyName) => {
if (context.getData('CData')) {
context.setData('CData', false);
} else {
context.setData('CData', true);
}
}
}
}
// 在工具栏“视图”选项卡添加sheet导航功能按钮
config.ribbon[5].buttonGroups.unshift({
'label': '自定义sheet导航',
'thumbnailClass': 'ribbon-thumbnail-chart-alt-text',
'commandGroup': {
'children': [
{
'direction': 'vertical',
'commands': [
'SheetNavi' // 与命令的commandName相同
]
}
]
}
});
// 定义sheet导航面板
var searchTemplate = {
templateName: 'searchTemplate',
content: [
{
type: 'TextBlock',
style: 'margin:10px;font-size:20px;font-weight:lighter;color:#08892c',
text: '导航'
},
{
type: 'Container',
children: [
{
type: 'Container',
margin: '10px 5px',
children: [
{
type: 'MyComponent', // 自定义Template控件,提供工作表导航信息布局能力
bindingPath: 'myComponentProcess'
}
]
}
]
}
]
};
GC.Spread.Sheets.Designer.registerTemplate('searchTemplate', searchTemplate);
// 导航面板命令
var sidePanelAuditCommands = {
searchPanelCommand: {
comamndName: 'searchPanelCommand',
enableContext: 'AllowEditObject',
visibleContext: 'CData' // 导航面包显示控制属性
}
};
// Template配置
var searchPanelConfig = {
position: 'right',
width: '315px',
command: 'searchPanelCommand',
uiTemplate: 'searchTemplate',
showCloseButton: true
};
Object.assign(config.commandMap, sidePanelAuditCommands);
config.sidePanels.push(searchPanelConfig);
designer.setConfig(config);
}
function MyTemplateComponent() {
console.log('MyTemplateComponent, constructor');
GC.Spread.Sheets.Designer.AtomicComponentBase.call(this, ...arguments);
this.inputContent = 0;
}
MyTemplateComponent.prototype = new GC.Spread.Sheets.Designer.AtomicComponentBase();
MyTemplateComponent.prototype.getTemplate = function(template) {
console.log('MyTemplateComponent, getTemplate: ', template);
var innerHTML = '<div id=\"sheetContainer\" class=\"sheet-info-container\"></div>';
return innerHTML;
}
MyTemplateComponent.prototype.onMounted = function(host, options) {
console.log('MyTemplateComponent, onMounted, host: ', host, '\noptions: ', options);
var hostDesigner = GC.Spread.Sheets.Designer.findControl('ss');
var hostSpread = hostDesigner.getWorkbook();
console.log('sheet count: ', hostSpread.getSheetCount());
var sheetNameContainer = document.createElement('div');
sheetNameContainer.className = 'tree-container';
// 创建搜索输入框
var searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.placeholder = '搜索表格、图表等...';
searchInput.style.cssText = `
display: block;
margin-bottom: 16px;
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
`;
sheetNameContainer.appendChild(searchInput);
// 生成树形结构
for (var i = 0; i < hostSpread.getSheetCount(); i++) {
// 创建节点容器
var treeNode = document.createElement('div');
treeNode.className = 'tree-node';
treeNode.dataset.sheetIndex = i;
// 创建节点头部
var nodeHeader = document.createElement('div');
nodeHeader.className = 'node-header';
// 创建展开/折叠按钮
var toggleBtn = document.createElement('button');
toggleBtn.className = 'toggle-btn';
toggleBtn.innerHTML = '<i class="fa fa-plus text-xs"></i>';
// 创建节点名称
var nodeName = document.createElement('span');
nodeName.className = 'node-name';
nodeName.innerText = hostSpread.getSheet(i).name();
// 组装节点头部
nodeHeader.appendChild(toggleBtn);
nodeHeader.appendChild(nodeName);
// 为节点头部添加点击事件(切换sheet)
nodeHeader.addEventListener('click', function(e) {
if (e.target.closest('.toggle-btn')) return; // 跳过按钮点击
e.stopPropagation();
const sheetName = this.querySelector('.node-name').textContent;
hostSpread.setActiveSheet(sheetName);
});
// 为按钮添加点击事件(展开/折叠)
toggleBtn.addEventListener('click', function(e) {
e.stopPropagation();
const node = this.closest('.tree-node');
const children = node.querySelector('.node-children');
const icon = this.querySelector('i');
if (node.classList.contains('expanded')) {
// 折叠节点
children.style.maxHeight = '0px';
node.classList.remove('expanded');
icon.classList.replace('fa-minus', 'fa-plus');
} else {
// 展开节点
const height = children.scrollHeight;
children.style.maxHeight = height + 'px';
node.classList.add('expanded');
icon.classList.replace('fa-plus', 'fa-minus');
}
});
// 创建子节点容器
var childrenNode = document.createElement('div');
childrenNode.className = 'node-children';
// 创建子节点内容
var childrenContent = document.createElement('div');
initTreeNode(hostSpread, i, childrenContent);
// 组装子节点
childrenNode.appendChild(childrenContent);
// 组装完整节点
treeNode.appendChild(nodeHeader);
treeNode.appendChild(childrenNode);
// 添加到容器
sheetNameContainer.appendChild(treeNode);
}
var container = document.getElementById('sheetContainer');
if (container.childElementCount === 1) {
container.appendChild(document.createElement('br'));
}
container.appendChild(sheetNameContainer);
// 添加搜索功能
searchInput.addEventListener('input', function(e) {
const searchText = e.target.value.toLowerCase();
const treeNodes = sheetNameContainer.querySelectorAll('.tree-node');
treeNodes.forEach(node => {
const nodeName = node.querySelector('.node-name').innerText.toLowerCase();
const nodeChildren = node.querySelector('.node-children');
const childItems = nodeChildren.querySelectorAll('.child-item');
// 检查当前节点名称是否匹配
const nodeMatch = nodeName.includes(searchText);
// 检查子项是否匹配
let childMatch = false;
childItems.forEach(item => {
const itemText = item.innerText.toLowerCase();
const match = itemText.includes(searchText);
item.style.display = match ? '' : 'none';
if (match) childMatch = true;
});
// 根据匹配结果显示或隐藏节点
if (nodeMatch || childMatch) {
node.style.display = '';
// 如果有子项匹配,自动展开节点
if (childMatch && searchText) {
node.classList.add('expanded');
const toggleBtn = node.querySelector('.toggle-btn');
const icon = toggleBtn.querySelector('i');
icon.classList.replace('fa-plus', 'fa-minus');
nodeChildren.style.maxHeight = nodeChildren.scrollHeight + 'px';
} else if (!searchText) {
// 搜索框为空时,恢复初始折叠状态
node.classList.remove('expanded');
const toggleBtn = node.querySelector('.toggle-btn');
const icon = toggleBtn.querySelector('i');
icon.classList.replace('fa-minus', 'fa-plus');
nodeChildren.style.maxHeight = '0px';
}
} else {
node.style.display = 'none';
}
});
});
}
// 辅助函数:创建树节点内容
function initTreeNode(hostSpread, sheetIndex, container) {
var tempSheet = hostSpread.getSheet(sheetIndex);
// 添加表格
tempSheet.tables.all().forEach(table => {
var childItem = document.createElement('div');
childItem.className = 'child-item';
childItem.innerText = table.name();
childItem.dataset.type = 'table';
childItem.addEventListener('click', function() {
const nodeName = this.closest('.tree-node').querySelector('.node-name').textContent;
hostSpread.setActiveSheet(nodeName);
changeSheet(hostSpread.getActiveSheet(), 'table', table.name());
});
container.appendChild(childItem);
});
// 添加数据透视表
tempSheet.pivotTables.all().forEach(pivotTable => {
var childItem = document.createElement('div');
childItem.className = 'child-item';
childItem.innerText = pivotTable.name();
childItem.dataset.type = 'pivotTable';
childItem.addEventListener('click', function() {
const nodeName = this.closest('.tree-node').querySelector('.node-name').textContent;
hostSpread.setActiveSheet(nodeName);
changeSheet(hostSpread.getActiveSheet(), 'pivotTable', pivotTable.name());
});
container.appendChild(childItem);
});
// 添加形状
tempSheet.shapes.all().forEach(shape => {
var childItem = document.createElement('div');
childItem.className = 'child-item';
childItem.innerText = shape.name();
childItem.dataset.type = 'shape';
childItem.addEventListener('click', function() {
const nodeName = this.closest('.tree-node').querySelector('.node-name').textContent;
hostSpread.setActiveSheet(nodeName);
changeSheet(hostSpread.getActiveSheet(), 'shape', shape.name());
});
container.appendChild(childItem);
});
}
/**
* 创建树最底层节点,即叶子节点,表示sheet中的图表、形状、Table、数据透视表等
*/
function initTreeNode(hostSpread, sheetIndex, childrenContent) {
var tempSheet = hostSpread.getSheet(sheetIndex);
var tableInfos = getSheetTableInfo(tempSheet);
if (tableInfos && tableInfos.length > 0) {
tableInfos.forEach(tableInfo => {
childrenContent.appendChild(createChildItem(tableInfo, hostSpread));
});
}
var pivotTablesInfo = getSheetPivotTablesInfo(tempSheet);
if (pivotTablesInfo && pivotTablesInfo.length > 0) {
pivotTablesInfo.forEach(pivotTable => {
childrenContent.appendChild(createChildItem(pivotTable, hostSpread));
});
}
var shapesInfo = getSheetShapesInfo(tempSheet);
console.log('shapesInfo: ', shapesInfo);
if (shapesInfo && shapesInfo.length > 0) {
shapesInfo.forEach(shape => {
childrenContent.appendChild(createChildItem(shape, hostSpread));
});
}
}
/**
* 获取Table摘要
*/
function getSheetTableInfo(sheet) {
var tablesInfo = [];
sheet.tables.all().forEach(table => {
tablesInfo.push({
type: 'table',
name: table.name(),
range: table.range()
});
})
return tablesInfo;
}
/**
* 获取PivotTable摘要
*/
function getSheetPivotTablesInfo(sheet) {
var pivotTablesInfo = [];
sheet.pivotTables.all().forEach(pivotTable => {
pivotTablesInfo.push({
type: 'pivotTable',
name: pivotTable.name(),
range: pivotTable.getRange()?.content
});
});
return pivotTablesInfo;
}
/**
* 获取Shape摘要(形状、图表、区域快照、图片都属于Shape)
*/
function getSheetShapesInfo(sheet) {
var shapesInfo = [];
sheet.shapes.all().forEach(shape => {
shapesInfo.push({
type: 'shape',
name: shape.name()
});
});
return shapesInfo;
}
/**
* 创建childItem
*/
function createChildItem(info, hostSpread) {
var childItem = document.createElement('div');
childItem.className = 'child-item';
childItem.innerText = info.name;
childItem.dataset.type = info.type;
childItem.addEventListener('click', function() {
console.log('child item clicked');
// 获取子元素对应的sheet名称
var nearestNodeChildren = this.closest('.node-children');
var nearestNodeHeader = nearestNodeChildren.previousElementSibling;
var nodeNameElement = nearestNodeHeader.querySelector('.node-name');
var nodeName = nodeNameElement.textContent;
console.log('node name---: ', nodeName);
// 跳转sheet后选中相应的元素
hostSpread.setActiveSheet(nodeName);
var curSheet = hostSpread.getActiveSheet();
changeSheet(curSheet, this.dataset.type, this.innerText);
});
return childItem;
}
/**
* 切换sheet,并选中sheet中的指定元素
*/
function changeSheet(sheet, elementType, elementName) {
console.log('elementType: ', elementType, ', elementName: ', elementName);
if (elementType === 'table') {
selectTable(sheet, elementName);
} else if (elementType === 'pivotTable') {
selectPivotTable(sheet, elementName);
} else if (elementType === 'chart') {
selectChart(sheet, elementName);
} else if (elementType === 'shape') {
selectShape(sheet, elementName);
}
}
/**
* 选中Table
*/
function selectTable(sheet, tableName) {
var table = sheet.tables.findByName(tableName);
if (table) {
var range = table.range();
sheet.setSelection(range.row, range.col, range.rowCount, range.colCount);
}
}
/**
* 选中数据透视表
*/
function selectPivotTable(sheet, pivotTableName) {
var range = sheet.pivotTables.get(pivotTableName)?.getRange().content;
if (range) {
sheet.setSelection(range.row, range.col, range.rowCount, range.colCount);
}
}
/**
* 选中形状
*/
function selectShape(sheet, shapeName) {
sheet.shapes.all().forEach(shape => {
if (shape.name() === shapeName) {
shape.isSelected(true);
} else {
shape.isSelected(false);
}
});
}
</script>
</html>