概述
本 Demo 展示了如何为集算表(TableSheet)创建自定义状态栏项。通过继承 StatusItem 类,实现了一个记录计数器状态项,能够实时显示选中行数、可见行数和总数据行数,并在用户选择单元格或筛选数据时自动更新。
实现思路
创建 Workbook 和 TableSheet,使用 DataManager 加载远程数据
定义 RecordCountItem 类,继承自 StatusItem 基类
重写 onCreateItemView 方法,创建状态栏项的 DOM 元素
重写 onBind 方法,绑定选择变化和筛选相关事件
重写 onUpdate 方法,实现状态栏项内容的动态更新
创建 StatusBar 实例,将自定义状态项添加到状态栏并绑定到 Workbook
代码解析
继承 StatusItem 创建自定义状态项
这段代码定义了 RecordCountItem 构造函数,继承自 StatusItem,并初始化了三个属性: _value(选中行数)、dataSourceLength(总数据行数)、visibleLength(可见行数)。
创建状态栏项视图元素
onCreateItemView 方法在状态栏项首次创建时调用,创建一个 span 元素并添加到容器中,显示当前的可见行数、总行数和选中行数。
绑定事件监听
onBind 方法绑定了三个事件:
SelectionChanged: 选择变化时更新选中行数
RangeFiltered: 筛选数据时更新可见行数和总行数
RangeFilterCleared: 清除筛选时更新可见行数和总行数
动态更新状态栏显示
onUpdate 方法在状态栏更新时调用,重新渲染状态栏项的 HTML 内容。
初始化状态栏
创建 StatusBar 实例时传入自定义的状态项数组,然后调用 bind 方法绑定到 Workbook。
运行效果
页面加载后,状态栏显示 "Rows: X of Y - Record Count: Z" 格式的信息
X 表示当前可见的行数,Y 表示数据总行数,Z 表示当前选中的行数
选择不同的单元格或区域时,Record Count 会实时更新为选中的行数
对数据进行筛选后,Rows 计数会更新为筛选后的可见行数
清除筛选后,Rows 计数恢复为总数据行数
API 参考
GC.Spread.Sheets.StatusBar.StatusItem
状态栏项基类,可通过继承创建自定义状态栏项。
onCreateItemView(container): 创建状态栏项的 DOM 元素
onBind(context): 绑定 Workbook 上下文和事件监听
onUpdate(): 更新状态栏项显示内容
GC.Spread.Sheets.StatusBar.StatusBar
构造函数: new StatusBar(hostElement, options)
bind(workbook): 将状态栏绑定到 Workbook
get(itemName): 获取指定名称的状态项
GC.Spread.Sheets.Events
SelectionChanged: 当单元格选择发生变化时触发
RangeFiltered: 当范围筛选完成时触发
RangeFilterCleared: 当范围筛选清除时触发
/*REPLACE_MARKER*/
/*DO NOT DELETE THESE COMMENTS*/
var tableName = "Customer";
var baseApiUrl = getBaseApiUrl();
var apiUrl = baseApiUrl + "/" + tableName;
var myView;
window.onload = function() {
var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
var statusBar = initStatusBar(spread);
initSpread(spread, statusBar);
};
function initSpread(spread, statusBar) {
spread.suspendPaint();
spread.options.autoFitType = GC.Spread.Sheets.AutoFitType.cellWithHeader;
//init a data manager
var dataManager = spread.dataManager();
//add customer table
var customerTable = dataManager.addTable("myTable", {
remote: {
read: {
url: apiUrl
}
}
});
//init a table sheet
var sheet = spread.addSheetTab(0, "TableSheet1", GC.Spread.Sheets.SheetType.tableSheet);
sheet.options.allowAddNew = false; //hide new row
//bind a view to the table sheet
myView = customerTable.addView("myView", [
{ value: "Id", width: 80 },
{ value: "CompanyName", width: 200, caption :"Company Name" },
{ value: "ContactName", width: 150, caption :"Contact" },
{ value: "ContactTitle", width: 200, caption :"Title" },
{ value: "Address", width: 200 },
{ value: "City", width: 150, caption :"City" },
{ value: "State", width: 100, caption :"State" },
{ value: "Region", width: 100, caption :"Region" }
]);
myView.fetch().then(function() {
sheet.setDataView(myView);
if (statusBar) {
var recordItem = statusBar.get("recordCountItem");
if (recordItem) {
recordItem.dataSourceLength = myView.length();
recordItem.visibleLength = myView.visibleLength();
recordItem.onUpdate();
}
}
});
spread.resumePaint();
}
function initStatusBar(spread) {
//define custom status item
var StatusItem = GC.Spread.Sheets.StatusBar.StatusItem;
function RecordCountItem(name, options) {
StatusItem.call(this, name, options);
this._value = 1;
this.dataSourceLength = 0;
this.visibleLength = 0;
}
RecordCountItem.prototype = new StatusItem();
RecordCountItem.prototype.onCreateItemView = function(container) {
var element = this._element;
if (!element) {
element = this._element = document.createElement("span");
element.innerHTML = " Rows: <b>" + this.visibleLength + "</b> of <b>" + this.dataSourceLength + "</b> - Record Count: <b>" + this._value + "</b>";
}
container.appendChild(element);
};
RecordCountItem.prototype.onBind = function(context) {
var _this = this;
context.bind(GC.Spread.Sheets.Events.SelectionChanged, function(e, args) {
_this._value = getRowCount(args.newSelections);
_this.onUpdate();
});
context.bind(GC.Spread.Sheets.Events.RangeFiltered, function (e, args) {
_this.dataSourceLength = myView.length();
_this.visibleLength = myView.visibleLength();
_this.onUpdate();
});
context.bind(GC.Spread.Sheets.Events.RangeFilterCleared, function (e, args) {
_this.dataSourceLength = myView.length();
_this.visibleLength = myView.visibleLength();
_this.onUpdate();
});
};
RecordCountItem.prototype.onUpdate = function() {
StatusItem.prototype.onUpdate.call(this);
this._element.innerHTML = " Rows: <b>" + this.visibleLength + "</b> of <b>" + this.dataSourceLength + "</b> - Record Count: <b>" + this._value + "</b>";
};
//init status bar
var recordCountItem = new RecordCountItem('recordCountItem', { menuContent: 'Record Count', value: 0 });
var statusBar = new GC.Spread.Sheets.StatusBar.StatusBar(document.getElementById('statusBar'), {
items: [recordCountItem]
});
statusBar.bind(spread);
return statusBar;
}
function getRowCount(selections) {
var selectedRows = {};
for (var i = 0; i < selections.length; i++) {
var row = selections[i].row;
var rowCount = selections[i].rowCount;
if (row === -1) {
return rowCount;
}
for (var c = 0; c < rowCount; c++) {
selectedRows[row + c] = true;
}
}
return Object.keys(selectedRows).length;
}
function getBaseApiUrl() {
return window.location.href.match(/http.+spreadjs\/SpreadJSTutorial\//)[0] + 'server/api';
}
<!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">
<!-- Promise Polyfill for IE, https://www.npmjs.com/package/promise-polyfill -->
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
<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-tablesheet/dist/gc.spread.sheets.tablesheet.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="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 id="statusBar"></div>
<div id="optionContainer" class="optionContainer">
</div>
</div>
</html>
.sample-tutorial {
position: relative;
height: 100%;
overflow: hidden;
}
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
#statusBar {
height: 25px;
width: 100%;
}
.sample-spreadsheets {
width: 100%;
height: calc(100% - 25px);
overflow: hidden;
}