XuY_Sheet 电子表格组件教程,吊打luckysheet
版本 : v1.0.0 | 核心: 基于Canvas的高性能Web电子表格引擎
XuY_Sheet是一个纯原生JavaScript实现的电子表格组件,采用Canvas 5层分层渲染,提供完整的电子表格功能,包括数据管理、22种公式函数、样式控制、事件系统和多工作表支持。所有核心功能均挂载在 window.XuY_Sheet 命名空间下。
目录
快速开始
1. 引入依赖与核心文件
<!-- 引入依赖库 -->
<script src="./vendor/xlsx.full.min.js"></script>
<script src="./vendor/fflate.min.js"></script>
<script src="./vendor/echarts.min.js"></script>
<!-- 引入核心JS与CSS -->
<script src="./assets/XuY_Sheet.js"></script>
<link rel="stylesheet" href="./assets/XuY_Sheet.css">
2. 初始化控件
在目标容器中调用 window.XuY_Sheet.setOption(obj) 快速构建并初始化控件。
<div id="demo"></div>
<script>
// 方式一:传入CSS选择器字符串(推荐)
const api = window.XuY_Sheet.setOption({
id: '#demo'
});
// 方式二:传入DOM节点
// const api = window.XuY_Sheet.setOption({
// id: document.getElementById('demo')
// });
// 可选:指定默认高度(容器无固定高度时生效,默认560px)
// const api = window.XuY_Sheet.setOption({
// id: '#demo',
// height: 720
// });
// 初始化后使用API
api.setCellValue(0, 0, 'Hello XuY_Sheet');
</script>
参数说明:
| 参数 |
类型 |
必填 |
说明 |
obj.id |
`HTMLElement |
string` |
是 |
obj.height |
number |
否 |
控件默认高度(px),默认560 |
obj.options |
object |
否 |
扩展配置保留位 |
兼容入口 : init(config)(旧{ container }写法)与 setXuY_Sheet(obj) 均等价于 setOption。
3. 访问实例与写入数据
// 通过公共API写入数据(推荐)
window.XuY_Sheet.setCellValue(0, 0, 'Hello XuY_Sheet');
// 或通过内部实例
const { tableStore, canvasRender } = window.ExcelApp.Instance;
tableStore.setCellValue(0, 1, 100);
tableStore.setCellValue(0, 2, 0, '=B1*1.2');
canvasRender.markDirty('text');
4. 监听事件
// 监听单元格值变化
window.XuY_Sheet.on('cellChanged', (data) => {
console.log(`单元格 (${data.row}, ${data.col}) 变化:`, data.cell);
});
// 监听工作表切换
window.XuY_Sheet.on('sheetActivate', (data) => {
console.log('切换到:', data.sheetName);
});
// 拦截编辑操作(可取消)
window.XuY_Sheet.on('cellEditBefore', (data) => {
if (data.row === 0) return false; // 阻止编辑第一行
});
核心API
全局访问入口
| 入口 |
类型 |
说明 |
window.XuY_Sheet |
公共API |
推荐的外部调用入口 |
window.ExcelApp.Instance |
内部实例 |
包含所有核心模块,适合高级定制 |
核心模块
| 模块 |
类名 |
职责 |
| 数据仓库 |
TableStore |
管理数据、样式、边框、合并区域,提供事件系统 |
| 选区管理 |
Selection |
管理活动单元格和选区范围 |
| 渲染引擎 |
CanvasRender |
5层Canvas分层渲染 |
| 公式引擎 |
Formula |
支持22种内置函数 |
| 剪贴板 |
Clipboard |
复制/剪切/粘贴 |
| 合并管理 |
MergeCell |
合并/拆分单元格 |
| 历史记录 |
History |
撤销/重做 |
单元格操作
// 获取/设置单元格值
tableStore.getCell(row, col);
tableStore.setCellValue(row, col, value, formula);
// 清除单元格
tableStore.clearCell(row, col); // 清除内容,保留格式
tableStore.clearCellAll(row, col); // 清除全部
tableStore.clearCellFormat(row, col); // 清除格式,保留内容
// 引用转换
tableStore.colIndexToLabel(27); // "AB"
tableStore.labelToColIndex('AB'); // 27
tableStore.getCellByRef('A1'); // { row: 0, col: 0 }
样式操作
样式属性一览:
| 属性 |
类型 |
默认值 |
说明 |
bgColor |
string |
#ffffff |
背景颜色 |
font |
string |
Microsoft YaHei |
字体族 |
fontSize |
number |
12 |
字体大小 |
bold |
boolean |
false |
粗体 |
italic |
boolean |
false |
斜体 |
underline |
string |
"none" |
下划线 |
strikethrough |
boolean |
false |
删除线 |
fontColor |
string |
#000000 |
字体颜色 |
hAlign |
string |
"left" |
水平对齐 (left/center/right) |
vAlign |
string |
"middle" |
垂直对齐 (top/middle/bottom) |
wrapText |
boolean |
false |
自动换行 |
// 设置单元格样式
tableStore.setCellStyle(0, 0, {
bold: true,
fontSize: 14,
fontColor: '#3b82f6',
bgColor: '#dbeafe',
hAlign: 'center'
});
// 设置边框
tableStore.setCellBorder(0, 0, {
top: { width: 2, color: '#000000', type: 'solid' },
bottom: { width: 1, color: '#666666', type: 'dashed' }
});
行列操作
// 插入/删除
tableStore.insertRow(1); // 在第2行前插入
tableStore.deleteRow(1); // 删除第2行
tableStore.insertCol(1); // 在B列前插入
tableStore.deleteCol(1); // 删除B列
// 隐藏/显示
tableStore.hideCol(1);
tableStore.showCol(1);
// 尺寸设置
tableStore.setColWidth(0, 120);
tableStore.setRowHeight(0, 30);
// 合并操作
tableStore.setMergeRange(0, 0, 2, 2); // 合并A1:C3
tableStore.removeMerge(0, 0); // 拆分
tableStore.isMerged(0, 0); // 判断是否在合并区域内
事件系统
// 注册/移除监听
tableStore.on('cellChanged', callback);
tableStore.off('cellChanged', callback);
// 触发事件
tableStore.emit('customEvent', data);
const allowed = tableStore.emitCancelable('customAction', data);
if (!allowed) console.log('操作被阻止');
序列化存储
// 导出为JSON
const json = tableStore.toJSON();
// 从JSON恢复
tableStore.fromJSON(data);
// 导出到服务端(Excel文件)
await tableStore.exportToServer('mySpreadsheet.xlsx');
文件导入
通过 importFile 方法导入 .xls / .xlsx / .csv 文件。
// 相对路径导入
const res = await window.XuY_Sheet.importFile('test-fixtures/data.xlsx');
// 绝对路径导入
await window.XuY_Sheet.importFile('C:\\Users\\data.xls');
await window.XuY_Sheet.importFile('/home/user/report.csv');
// 返回值示例
console.log(res.sheetCount, res.totalRows, res.totalCols);
/* 返回:
{
success: true,
fileName: 'data.xlsx',
format: 'xlsx',
sheetCount: 3,
totalRows: 100,
totalCols: 10,
sheets: [{ name: 'Sheet1', rows: 50, cols: 5 }, ...]
}
*/
// 错误处理
try {
await window.XuY_Sheet.importFile('not_exists.xlsx');
} catch (e) {
console.error('导入失败:', e.message);
}
支持格式与限制:
| 格式 |
扩展名 |
说明 |
| Excel 2007+ |
.xlsx |
完整还原样式、合并区域、多工作表 |
| Excel 97-2003 |
.xls |
还原字体、边框、对齐等 |
| CSV |
.csv |
自动识别编码(UTF-8/GBK/GB18030) |
控制API
顶部菜单显隐
// 显示/隐藏/切换
XuY_Sheet.showTopMenu(); // true
XuY_Sheet.hideTopMenu(); // false
XuY_Sheet.toggleTopMenu(); // 返回切换后的状态
XuY_Sheet.isTopMenuVisible(); // 查询状态
XuY_Sheet.setTopMenuVisible(false);
单元格编辑权限
// 锁定/解锁单元格
XuY_Sheet.lockCell(2, 0); // 锁定A3
XuY_Sheet.unlockCell(2, 0); // 解锁A3
XuY_Sheet.isCellLocked(2, 0); // true/false
XuY_Sheet.isCellEditable(2, 0); // 综合判断
XuY_Sheet.getLockedCells(); // 获取所有锁定单元格
行列尺寸冻结
// 冻结行高/列宽
XuY_Sheet.freezeRowHeight(0); // 冻结第1行行高
XuY_Sheet.unfreezeRowHeight(0); // 解除
XuY_Sheet.isRowHeightFrozen(0); // true/false
XuY_Sheet.freezeColWidth(1); // 冻结B列列宽
XuY_Sheet.unfreezeColWidth(1);
XuY_Sheet.isColWidthFrozen(1);
全局只读开关
XuY_Sheet.setGlobalReadonly(true); // 全表只读
XuY_Sheet.isGlobalReadonly(); // true
XuY_Sheet.setGlobalReadonly(false); // 恢复(单元格锁仍生效)
优先级:单元格可编辑 = 全局只读未开启 && 单元格未被单独锁定
内容查询与替换
// 搜索(不区分大小写)
XuY_Sheet.searchCells('A1:C3', 'XuY_Sheet');
// => ["A1", "B2"]
// 替换(跳过锁定与只读单元格)
XuY_Sheet.replaceCells('A1:C3', '旧文本', '新文本');
// => 返回实际被替换的坐标数组
生命周期钩子
数据事件
| 事件名 |
参数 |
说明 |
cellChanged |
{ row, col, cell } |
单元格值改变后触发 |
cellUpdated |
{ row, col, oldValue, newValue, oldFormula, newFormula } |
包含新旧值对比 |
styleChanged |
{ row, col, style, border } |
样式/边框改变后触发 |
dataLoadBefore |
{ fileName, fileType, fileSize } |
文件解析开始前触发 |
dataLoadAfter |
{ fileName, sheetCount, duration, success, error } |
加载完成后触发 |
编辑事件
| 事件名 |
参数 |
可取消 |
cellEditBefore |
{ row, col, cellRef, source } |
✅ |
cellMousedownBefore |
{ row, col, cellRef, button, ctrlKey, shiftKey } |
✅ |
cellClick |
{ row, col, cellRef } |
❌ |
cellDblClick |
{ row, col, cellRef } |
❌ |
工作表事件
| 事件名 |
参数 |
说明 |
sheetActivate |
{ activeSheetIndex, previousIndex, sheetName } |
工作表切换 |
sheetChanged |
{ action, activeSheetIndex, removedIndex, fromIndex, toIndex } |
结构变化 |
sheetRenamed |
{ index, name } |
重命名 |
dataReset |
{} |
数据整体重置 |
结构事件
| 事件名 |
参数 |
说明 |
rowInserted |
{ beforeRow } |
插入行 |
rowDeleted |
{ row } |
删除行 |
colInserted |
{ beforeCol } |
插入列 |
colDeleted |
{ col } |
删除列 |
sizeChanged |
{ type, index, size } |
行高/列宽变化 |
mergeChanged |
{ startRow, startCol, endRow, endCol, action } |
合并/拆分 |
粘贴事件
| 事件名 |
参数 |
可取消 |
rangePasteBefore |
{ startRow, startCol, endRow, endCol, clipboardData, dataPreview } |
✅ |
公式函数
聚合函数
| 函数 |
语法示例 |
说明 |
SUM |
=SUM(A1:A10) |
求和 |
AVERAGE |
=AVERAGE(A1:A10) |
平均值 |
MAX |
=MAX(A1:A10) |
最大值 |
MIN |
=MIN(A1:A10) |
最小值 |
COUNT |
=COUNT(A1:A10) |
数值个数 |
COUNTA |
=COUNTA(A1:A10) |
非空个数 |
逻辑函数
| 函数 |
语法示例 |
说明 |
IF |
=IF(B1>=60,"及格","不及格") |
条件判断,支持嵌套 |
数学函数
| 函数 |
语法示例 |
说明 |
ROUND |
=ROUND(A1, 2) |
四舍五入 |
ABS |
=ABS(A1) |
绝对值 |
INT |
=INT(A1) |
向下取整 |
MOD |
=MOD(A1, 2) |
取余 |
POWER |
=POWER(A1, 2) |
幂运算 |
SQRT |
=SQRT(A1) |
平方根 |
文本函数
| 函数 |
语法示例 |
说明 |
CONCATENATE |
=CONCATENATE(A1, " ", B1) |
拼接文本 |
LEFT |
=LEFT(A1, 6) |
左侧提取 |
RIGHT |
=RIGHT(A1, 6) |
右侧提取 |
LEN |
=LEN(A1) |
字符长度 |
TRIM |
=TRIM(A1) |
去除首尾空格 |
UPPER |
=UPPER(A1) |
转大写 |
LOWER |
=LOWER(A1) |
转小写 |
日期时间函数
| 函数 |
语法示例 |
说明 |
NOW |
=NOW() |
当前日期时间 |
TODAY |
=TODAY() |
当前日期 |
常见问题
Q: 如何保护某些单元格不被编辑?
方法一: 监听 cellEditBefore 事件
window.XuY_Sheet.on('cellEditBefore', (data) => {
if (data.row === 0) return false; // 禁止编辑第一行
});
方法二: 使用便捷方法
XuY_Sheet.setCellReadonly(0, 0, true);
Q: 数据是否会自动保存?
XuY_Sheet已移除localStorage自动缓存机制,每次加载均为默认空表。如需持久化:
// 获取数据快照
const json = tableStore.toJSON();
// 或导出到服务端
await tableStore.exportToServer('myFile.xlsx');
Q: 如何在自定义容器中初始化?
// 方式一:CSS选择器字符串
window.XuY_Sheet.setOption({ id: '#myContainer' });
// 方式二:DOM节点
window.XuY_Sheet.setOption({ id: document.getElementById('myContainer') });
Q: 支持哪些公式?
内置22种公式函数,支持四则运算、比较运算、单元格与区域引用、函数嵌套。
Q: 公共API和内部实例有什么区别?
|
window.XuY_Sheet |
window.ExcelApp.Instance |
| 定位 |
推荐的外部调用入口 |
内部实例对象 |
| 包含 |
常用封装方法 |
所有核心模块(tableStore, selection, canvasRender...) |
| 适用 |
日常操作 |
深度定制场景 |
Q: 界面不刷新/数据更新后无变化?
在修改数据或样式后,需要标记脏层并触发重绘:
tableStore.setCellStyle(0, 0, { bold: true });
canvasRender.markDirty('text');
canvasRender.markDirty('background');
canvasRender.forceRenderAll();
完整API文档: 如需查看所有详细信息和示例,请参考原HTML文档。
Gitee、Github搜索 XuY_Sheet