分组列

SpreadJS 支持在表格中展示具有分层结构的数据,通过分组列功能可以将数据以树形结构呈现。用户可以通过点击展开/折叠标记来控制数据的显示层级,还可以通过复选框进行批量选择操作,非常适合用于展示项目任务分解、组织架构、文档目录等具有层级关系的数据。

概述 本 Demo 展示了 SpreadJS 的分组列(Outline Column)功能,实现了一个项目工作分解结构(WBS)的树形数据展示。Demo 演示了如何通过 outlineColumn API 配置树形结构,以及如何动态调整分组列的各种选项,包括显示指示器、复选框、自定义图标、设置最大级别等。 实现思路 加载预设的项目任务数据,通过 setDataSource 方法绑定数据源 根据数据中的 level 字段,为每个单元格设置文本缩进(textIndent),建立数据的层级关系 调用 outlineColumn.options() 方法配置分组列,指定列索引、显示选项和图标 调用 outlineColumn.refresh() 刷新分组列显示 绑定界面事件处理器,允许用户动态修改分组列的各项配置 代码解析 配置分组列选项 这段代码初始化了分组列的核心配置。columnIndex 指定第 0 列作为分组列,showImage 和 showCheckBox 分别控制是否显示层级图标和复选框。images 数组为不同层级设置图标(每个元素对应一个级别),expandIndicator 和 collapseIndicator 定义展开和折叠状态的指示器图片。maxLevel 设置最大层级为 2,超过此层级的数据将不再细分。 设置文本缩进建立层级关系 这段代码通过遍历数据源,根据每行的 level 字段设置文本缩进。textIndent(level) 方法的参数值越大,缩进越深,从而在视觉上体现层级关系。同时根据层级设置不同的背景色,提升可读性。 动态修改分组列配置 这段代码展示了如何动态修改分组列配置。每次修改选项后,都需要调用 outlineColumn.refresh() 方法来刷新显示。Demo 中提供了多个复选框和按钮,允许用户实时调整指示器、复选框、图标的显示状态,以及上传自定义图标。 运行效果 表格左侧显示项目任务的树形结构,不同层级有不同的缩进和背景色 点击分组标记可以展开或折叠该分组下的子项 点击复选框可以选中或取消选中任务,选中状态会联动影响其所有子任务 通过右侧面板可以动态控制分组列的显示选项:显示/隐藏指示器、复选框、图像 可以上传自定义图片来替换不同层级的图标和展开/折叠指示器 可以修改最大级别参数来控制树形结构的深度 API 参考 outlineColumn.options() 获取或设置大纲列的选项。 outlineColumnOptions.columnIndex:指定作为分组列的列索引 outlineColumnOptions.showIndicator:是否显示展开/折叠指示器 outlineColumnOptions.showCheckBox:是否显示复选框 outlineColumnOptions.showImage:是否显示层级图标 outlineColumnOptions.images:字符串数组,为不同层级指定图标(URL 或 Base64) outlineColumnOptions.expandIndicator:展开指示器图片(URL 或 Base64) outlineColumnOptions.collapseIndicator:折叠指示器图片(URL 或 Base64) outlineColumnOptions.maxLevel:最大层级数,默认为 10 outlineColumn.refresh() 刷新分组列的显示。修改分组列配置后需要调用此方法使更改生效。
window.onload = function() { var spread = new GC.Spread.Sheets.Workbook(_getElementById('ss'), { sheetCount: 2 }); initSpread(spread); }; function initSpread(spread) { var sheet = spread.getActiveSheet(); sheet.suspendPaint(); loadData(sheet); initOutlineColumn(sheet); setOutlineColumnOptions(sheet); sheet.getRange(0,3,25,3).hAlign(GC.Spread.Sheets.HorizontalAlign.center); sheet.resumePaint(); } function loadData(sheet) { var sd = data; sheet.setDataSource(sd); sheet.setColumnCount(6); sheet.setColumnWidth(0, 150); sheet.setColumnWidth(1, 350); sheet.setColumnWidth(2, 150); sheet.setColumnWidth(3, 150); sheet.setColumnWidth(4, 150); sheet.setColumnWidth(5, 150); for (var r = 0; r < sd.length; r++) { var level = sd[r].level; if(level==0) { sheet.getRange(r,0,1,7).backColor("#CCCCCC"); } else if(level==1) { sheet.getRange(r,0,1,7).backColor("#EEEEEE"); } sheet.getCell(r, 0).textIndent(level); } } const defaultImages = ['$DEMOROOT$/spread/source/images/task-1.png', '$DEMOROOT$/spread/source/images/task-2.png', '$DEMOROOT$/spread/source/images/task-3.png']; const defaultExpandIndicator = '$DEMOROOT$/spread/source/images/increaseIndicator.png'; const defaultCollapseIndicator = '$DEMOROOT$/spread/source/images/decreaseIndicator.png'; function initOutlineColumn(sheet) { sheet.outlineColumn.options({ columnIndex: 0, showImage: true, showCheckBox: true, images: defaultImages, expandIndicator: defaultExpandIndicator, collapseIndicator: defaultCollapseIndicator, maxLevel: 2 }); sheet.showRowOutline(false); sheet.outlineColumn.refresh(); } function setOutlineColumnOptions(sheet) { var picture1Url = defaultImages[0]; var picture2Url = defaultImages[1]; var picture3Url = defaultImages[2]; var indicator1Url = defaultExpandIndicator; var indicator2Url = defaultCollapseIndicator; var options = sheet.outlineColumn.options(); _getElementById('maxLevel').value = options.maxLevel; _getElementById('showIndicator').addEventListener('change', function() { sheet.outlineColumn.options().showIndicator = _getElementById('showIndicator').checked; sheet.outlineColumn.refresh(); }); _getElementById('showCheckBox').addEventListener('change', function() { sheet.outlineColumn.options().showCheckBox = _getElementById('showCheckBox').checked; sheet.outlineColumn.refresh(); }); _getElementById('showImage').addEventListener('change', function() { sheet.outlineColumn.options().showImage = _getElementById('showImage').checked; sheet.outlineColumn.refresh(); }); _getElementById('customIndicator').addEventListener('change', function() { if (!_getElementById('customIndicator').checked) { _getElementById('setIndicators').setAttribute('disabled', true); _getElementById('indicator1').setAttribute('disabled', true); _getElementById('indicator2').setAttribute('disabled', true); sheet.outlineColumn.options().expandIndicator = null; sheet.outlineColumn.options().collapseIndicator = null; sheet.outlineColumn.refresh(); } else { _getElementById('setIndicators').removeAttribute('disabled'); _getElementById('indicator1').removeAttribute('disabled'); _getElementById('indicator2').removeAttribute('disabled'); _getElementById('setIndicators').addEventListener('click', _handel()); } }); _getElementById('setIndicators').addEventListener('click', _handel); function _handel(){ sheet.outlineColumn.options().expandIndicator =indicator1Url; sheet.outlineColumn.options().collapseIndicator = indicator2Url; sheet.outlineColumn.refresh(); } _getElementById('setMaxLevel').addEventListener('click', function() { sheet.outlineColumn.options().maxLevel = parseInt(_getElementById('maxLevel').value); sheet.outlineColumn.refresh(); }); _getElementById('setImages').addEventListener('click', function() { sheet.outlineColumn.options().images = [ picture1Url, picture2Url, picture3Url ]; sheet.outlineColumn.refresh(); }); _getElementById('image1').addEventListener('change', function() { var file = this.files[0]; if (!/image\/\w+/.test(file.type)) { alert('The file must be an image!'); return false; } var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e) { picture1Url = this.result; }; }); _getElementById('image2').addEventListener('change', function() { var file = this.files[0]; if (!/image\/\w+/.test(file.type)) { alert('The file must be an image!'); return false; } var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e) { picture2Url = this.result; }; }); _getElementById('image3').addEventListener('change', function() { var file = this.files[0]; if (!/image\/\w+/.test(file.type)) { alert('The file must be an image!'); return false; } var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e) { picture3Url = this.result; }; }); _getElementById('indicator1').addEventListener('change', function() { var file = this.files[0]; if (!/image\/\w+/.test(file.type)) { alert('The file must be an image!'); return false; } var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e) { indicator1Url = this.result; }; }); _getElementById('indicator2').addEventListener('change', function() { var file = this.files[0]; if (!/image\/\w+/.test(file.type)) { alert('The file must be an image!'); return false; } var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function(e) { indicator2Url = this.result; }; }); } function _getElementById(id) { return document.getElementById(id); }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta name="spreadjs culture" content="zh-cn" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/zh/purejs/node_modules/@grapecity-software/spread-sheets-resources-zh/dist/gc.spread.sheets.resources.zh.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/data/outlineColumn-wbs.js" type="text/javascript"></script> <script src="app.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <div class="sample-tutorial"> <div id="ss" class="sample-spreadsheets"></div> <div class="options-container"> <div class="option-row"> <label>大纲列选项:</label> </div> <hr> <div class="option-row"> <div class="option-row"> <input type="checkbox" id="showIndicator" checked="checked" /> <label for="showIndicator">显示指示器</label> </div> <div class="option-row"> <input type="checkbox" id="showCheckBox" checked="checked" /> <label for="showCheckBox">显示复选框</label> </div> <div class="option-row"> <input type="checkbox" id="showImage" checked="checked" /> <label for="showImage">显示图像</label> </div> <div class="option-row"> <input type="checkbox" id="customIndicator" checked="checked" /> <label for="customIndicator" style="width: auto">允许自定义指示器</label> </div> <div class="option-row"> <label for="maxLevel" class="rightAlignLabel">最大级别:</label> <input type="text" id="maxLevel" style="width: 90px;" /> </div> <div class="option-row" style="padding-top: 6px"> <input type="button" id="setMaxLevel" value="设置" /> </div> </div> <div class="option-row"> <label>自定义图像设置:</label> </div> <hr> <div class="option-row"> <label for="image1" class="rightAlignLabel"> 1 级图像: </label> <input type="file" name="img1" id="image1" /> </div> <div class="option-row"> <label for="image2" class="rightAlignLabel"> 2 级图像: </label> <input type="file" name="img2" id="image2" /> </div> <div class="option-row"> <label for="image3" class="rightAlignLabel"> 3 级图像: </label> <input type="file" name="img3" id="image3" /> </div> <div class="option-row" style="padding-top: 6px"> <input type="button" id="setImages" value="设置" /> </div> <div class="option-row"> <label>自定义指示器图像设置:</label> </div> <hr> <div class="option-row"> <label for="indicator1" class="rightAlignLabel">展开图像:</label> <input type="file" name="in1" id="indicator1" /> </div> <div class="option-row"> <label for="indicator2" class="rightAlignLabel">折叠图像:</label> <input type="file" name="in2" id="indicator2" /> </div> <div class="option-row" style="padding-top: 6px"> <input type="button" id="setIndicators" value="设置" /> </div> </div> </div> </body> </html>
.colorLabel { background-color: #F4F8EB; } .rightAlignLabel { width: 120px; text-align: right; margin-right: 8px; } input[type="text"] { width: 190px; } .sample-tutorial { position: relative; height: 100%; overflow: hidden; } .sample-spreadsheets { width: calc(100% - 280px); height: 100%; overflow: hidden; float: left; } .options-container { float: right; width: 280px; padding: 12px; height: 100%; box-sizing: border-box; background: #fbfbfb; overflow: auto; } .option-row { font-size: 14px; padding: 5px; margin-top: 10px; } label { margin-bottom: 6px; } input { padding: 4px 6px; } input[type=button] { margin-top: 6px; width: 190px; } body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; }