概述
本 Demo 展示了如何扩展 SpreadJS 的上下文菜单,通过操作 spread.contextMenu.menuData 数组动态添加或移除自定义菜单项。Demo 中演示了两个自定义菜单项:"设置单元格格式"(通过注册命令字符串实现)和"打开对话框"(通过函数直接实现),并提供了复选框来控制菜单项的添加和移除。
实现思路
通过复选框的 onchange 事件控制菜单项的添加和移除
添加菜单项前,先遍历 menuData 检查是否已存在,避免重复添加
使用 menuData.push() 添加新菜单项,使用 menuData.splice() 移除菜单项
对于"设置单元格格式"菜单项:注册命令字符串到 commandManager,然后实现命令的 execute 方法
对于"打开对话框"菜单项:直接将函数作为 command 属性值
代码解析
添加菜单项并注册命令
这段代码展示了如何通过命令字符串的方式添加菜单项。首先定义菜单项配置,其中 command 属性使用字符串标识符。然后通过 commandManager().register() 注册该命令的具体实现逻辑。
使用函数作为命令
这种方式更简洁,直接将函数作为 command 属性值,无需注册命令。适用于不需要撤销/重做功能的简单操作。
移除菜单项
通过遍历 menuData 数组,找到匹配 name 属性的菜单项,使用 splice() 方法移除。
运行效果
页面右侧提供两个复选框选项,用于控制菜单项的添加和移除
勾选"添加 '设置单元格格式' 菜单项"后,右键菜单中会显示该选项,点击后会将选中单元格设置为红色背景
勾选"添加 '打开对话框' 菜单项"后,右键菜单中会显示该选项,点击后会弹出一个简单的对话框
取消勾选复选框后,对应的菜单项会从右键菜单中移除
所有操作仅影响表格区域(viewport),在行头、列头等其他区域右键不会显示这些自定义菜单项
API 参考
menuData 属性
表示上下文菜单的菜单项数组,每个菜单项遵循 JSON Schema 格式,支持以下属性:
text:菜单项显示的文本
name:菜单项的唯一标识
command:命令名称(字符串)或执行函数
workArea:菜单项显示区域,可选值包括 viewport、rowHeader、colHeader、corner 等
register 方法
向命令管理器注册自定义命令。
name:命令名称(字符串)
command:命令对象,包含 canUndo 和 execute 属性
key、ctrl、shift、alt、meta:可选,用于设置快捷键
window.onload = function () {
var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), {sheetCount: 2});
initSpread(spread);
};
function initSpread(spread) {
initEvents(spread);
}
function initEvents(spread) {
document.getElementById('addFormatCells').onchange = function () {
var commandRemoved = false;
spread.contextMenu.menuData.forEach(function (item, index) {
if (item && item.name === "markWithRedBg") {
spread.contextMenu.menuData.splice(index, 1);
commandRemoved = true;
}
});
if (commandRemoved) {
return;
}
var commandManager = spread.commandManager();
var markWithRedBg = {
text: "设置单元格格式",
name: "markWithRedBg",
command: "markWithRedBg",
workArea: "viewport"
};
spread.contextMenu.menuData.push(markWithRedBg);
var markWithRedBgCommand = {
canUndo: false,
execute: function () {
var style = new GC.Spread.Sheets.Style();
style.name = 'style1';
style.backColor = 'red';
var sheet = spread.getActiveSheet();
sheet.suspendPaint();
var selections = sheet.getSelections();
var selectionIndex = 0, selectionCount = selections.length;
for (; selectionIndex < selectionCount; selectionIndex++) {
var selection = selections[selectionIndex];
for (var i = selection.row; i < (selection.row + selection.rowCount); i++) {
for (var j = selection.col; j < (selection.col + selection.colCount); j++) {
sheet.setStyle(i, j, style, GC.Spread.Sheets.SheetArea.viewport);
}
}
}
sheet.resumePaint();
}
};
commandManager.register("markWithRedBg", markWithRedBgCommand, null, false, false, false, false);
};
document.getElementById('addOpenDialog').onchange = function () {
var commandRemoved = false;
spread.contextMenu.menuData.forEach(function (item, index) {
if (item && item.name === "openDialog") {
spread.contextMenu.menuData.splice(index, 1);
commandRemoved = true;
}
});
if (commandRemoved) {
return;
}
var openDialog = {
text: "打开对话框",
name: "openDialog",
command: showLoginDialog,
workArea: "viewport"
};
spread.contextMenu.menuData.push(openDialog);
};
}
function showLoginDialog() {
if (document.getElementsByClassName('loginBox').length === 0) {
var loginBox = document.createElement('div');
loginBox.className = 'loginBox';
loginBox.innerHTML = '<label for="Dialog" class="loginBoxLabel">对话框</label>' +
'<input type="button" id="okBtn" class="btn-primary button" value="确定">' +
'<input type="button" id="cancelBtn" class="btn-primary button" value="取消">';
document.body.appendChild(loginBox);
document.getElementById('okBtn').onclick = function (){
loginBox.style.display = 'none';
};
document.getElementById('cancelBtn').onclick = function (){
loginBox.style.display = 'none';
};
}
var loginBoxEle = document.getElementsByClassName('loginBox')[0];
if (loginBoxEle.style.display === 'none') {
loginBoxEle.style.display = 'block';
}
}
<!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="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">
<p>右键单击任意单元格以打开其上下文菜单。然后,您可以添加或移除以下演示选项之一:</p>
<div id="settingsDiv">
<div class="option-row">
<input id="addFormatCells" type="checkbox" />
<label for="addFormatCells">添加 "设置单元格格式" 菜单项</label>
</div>
<div class="option-row">
<input id="addOpenDialog" type="checkbox" />
<label for="addOpenDialog">添加 "打开对话框" 菜单项</label>
</div>
</div>
</div>
</div>
</body>
</html>
.option-row {
font-size: 14px;
padding: 5px;
margin-top: 10px;
}
.buttonStyle{
width:240px;
height:30px;
}
.loginBox {
position: absolute;
left: 35%;
top: 30%;
background-color: white;
padding: 20px;
border: 1px solid #c6c6c6;
box-shadow: rgba(0, 0, 0, 0.4) 1px 2px 5px;
z-index: 2000;
height: 100px;
width: 200px;
}
.loginBoxLabel {
display: inline-block;
width: 200px;
text-align: center;
}
.button{
width: 30%;
margin: 60px 10%;
text-align: center;
}
.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;
margin-top: 10px;
}
label {
margin-bottom: 6px;
}
input {
padding: 4px 6px;
}
input[type=button] {
margin-top: 6px;
}
p{
padding:2px 10px;
background-color:#F4F8EB;
}
body {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}