$excel - Excel 表格
更新: 2026/8/9 字数: 0 字 时长: 0 分钟
$excel 模块提供了完整的 Excel 文件操作能力,支持在 Android 环境中创建、编辑、查询和处理 Excel 文件。无论是简单的数据读写,还是复杂的样式设置、数据验证、图表生成,都可以通过简洁的 API 轻松完成。
- 文件操作:打开、创建、保存、另存为 Excel 文件,支持密码保护
- 工作表管理:创建、删除、重命名、复制工作表,支持冻结窗格和筛选
- 数据读写:单元格级别的数据读写,支持批量写入和读取
- 数据查询:类似 SQL 的条件查询,支持
>,<,>=,<=,=,!=,LIKE,AND,OR - 样式设置:字体、颜色、背景、边框、对齐、数字格式等完整样式支持
- 数据验证:下拉列表、整数、小数、日期、时间、文本长度等验证规则
- 合并单元格:合并、取消合并、重叠检测
- 图片插入:支持从文件路径、字节数组、Bitmap 插入图片
- 批注和超链接:单元格批注和超链接支持
- 行/列操作:插入、删除、隐藏、行高列宽设置
本模块基于 Apache POI 实现,支持两种 Excel 文件格式:
| 格式 | 扩展名 | 说明 |
|---|---|---|
| XLSX | .xlsx | 推荐使用。基于 XML 的开放格式,支持更多功能(如更多行/列、更好的样式支持、数据验证、图表等),文件体积更小,兼容性更好。 |
| XLS | .xls | 旧版二进制格式,功能有限(最大 65536 行、256 列),样式和验证支持较弱。 |
推荐
强烈建议使用 .xlsx 格式,以获得更好的功能支持和兼容性。本模块的密码保护、高级样式、数据验证等高级功能均对 .xlsx 提供完整支持。
$excel.open(options[, callback])
options{Object} 文件配置path{string} 文件路径(绝对路径,如/sdcard/example.xlsx)readOnly{boolean} 是否以只读模式打开,默认falsepassword{string} 文件密码(用于打开或保存密码保护的 Excel 文件,目前仅支持.xlsx格式)
callback{Object} 回调对象(可选)onOpen(excel)打开成功后回调,excel为 Excel 对象
- 返回 Excel
打开或创建一个 Excel 文件。如果文件不存在,则会自动创建一个新的 Excel 文件。
注意
只读模式下无法调用 excel.save()。
var excel = $excel.open({
path: "/sdcard/data.xlsx"
});
// 修改数据(示例:添加一个 Sheet 并写入内容)
var sheet = excel.sheet("数据");
if (!sheet) {
sheet = excel.createSheet("数据");
}
sheet.cell(0, 0).setValue("姓名");
sheet.cell(0, 1).setValue("年龄");
sheet.cell(1, 0).setValue("张三");
sheet.cell(1, 1).setValue(25);
// 保存更改到磁盘
excel.save();
console.log("✅ 数据已保存到 data.xlsx");
excel.close();var path = "/sdcard/report.xlsx";
// 准备示例数据(仅在文件不存在时创建)
if (!files.exists(path)) {
var excel = $excel.open({ path: path });
var sheet = excel.sheet("报表");
if (!sheet) {
sheet = excel.createSheet("报表");
}
sheet.cell(0, 0).setValue("姓名");
sheet.cell(0, 1).setValue("年龄");
sheet.cell(1, 0).setValue("张三");
sheet.cell(1, 1).setValue(25);
excel.save();
excel.close();
}
// 以只读模式打开
var excel = $excel.open({
path: path,
readOnly: true
});
// 读取数据
var sheet = excel.sheet("报表");
if (sheet) {
console.log("姓名:", sheet.cell(1, 0).value());
console.log("年龄:", sheet.cell(1, 1).value());
}
excel.close();var path = "/sdcard/secret.xlsx";
var password = "123456";
// 准备示例文件(仅首次运行时执行)
if (!files.exists(path)) {
var excel = $excel.open({
path: path,
password: password
});
var sheet = excel.createSheet("机密数据");
sheet.cell(0, 0).setValue("姓名");
sheet.cell(0, 1).setValue("张三");
excel.save();
excel.close();
}
// 使用密码打开文件
var excel = $excel.open({
path: path,
password: "123456"
});
var sheet = excel.sheet("机密数据");
if (sheet) {
console.log("姓名:", sheet.cell(0, 1).value());
}
excel.close();var excel = $excel.open({
path: "/sdcard/data.xlsx"
}, {
onOpen: function (excel) {
console.log("✅ 文件打开成功");
console.log("📊 Sheet 数量:", excel.sheetCount());
// 在回调中操作并保存
var sheet = excel.sheet("新增Sheet");
if (!sheet) sheet = excel.createSheet("新增Sheet");
sheet.cell(0, 0).setValue("测试数据");
excel.save();
console.log("✅ 数据已保存");
}
});
excel.close();Excel
由 $excel.open() 返回,代表一个 Excel 工作簿。
Excel.sheetCount()
- 返回 {number}
获取工作簿中的工作表总数。
console.log("Sheet 总数:", excel.sheetCount());Excel.sheetNames()
- 返回 {Array<string>}
获取所有工作表的名称列表。
var names = excel.sheetNames();
for (var i = 0; i < names.length; i++) {
console.log("📄", names[i]);
}Excel.sheet(name)
name{string} 工作表名称- 返回 ExcelSheet |
null
根据名称获取工作表。如果不存在则返回 null。
var sheet = excel.sheet("数据表");
if (sheet) {
console.log("✅ 找到工作表");
} else {
console.log("❌ 工作表不存在");
}Excel.sheet(index)
index{number} 工作表索引(从 0 开始)- 返回 ExcelSheet |
null
根据索引获取工作表。如果越界则返回 null。
// 获取第一个工作表
var firstSheet = excel.sheet(0);
console.log("第一个工作表:", firstSheet.name());Excel.activeSheet()
- 返回 ExcelSheet
获取当前激活的工作表。如果没有激活的 Sheet,则返回第一个 Sheet。
var active = excel.activeSheet();
console.log("当前激活的 Sheet:", active.name());Excel.setActiveSheet(index)
index{number} 工作表索引
通过索引设置激活的工作表。
excel.setActiveSheet(1);Excel.setActiveSheet(name)
name{string} 工作表名称
通过名称设置激活的工作表。
excel.setActiveSheet("数据表");Excel.createSheet(name)
name{string} 新工作表的名称- 返回 ExcelSheet
创建一个新的工作表。
var newSheet = excel.createSheet("新表格");
console.log("✅ 已创建:", newSheet.name());Excel.hasSheet(name)
name{string} 工作表名称- 返回 {boolean}
判断指定名称的工作表是否存在。
if (excel.hasSheet("数据表")) {
console.log("✅ 数据表已存在");
}Excel.removeSheet(index)
index{number} 工作表索引
根据索引删除工作表。
excel.removeSheet(0);Excel.removeSheet(name)
name{string} 工作表名称
根据名称删除工作表。
excel.removeSheet("临时表");Excel.renameSheet(index, newName)
index{number} 工作表索引newName{string} 新名称
重命名工作表。
excel.renameSheet(0, "主数据");Excel.renameSheet(oldName, newName)
oldName{string} 原名称newName{string} 新名称
根据名称重命名工作表。
excel.renameSheet("Sheet1", "销售数据");Excel.copySheet(sourceIndex, newName)
sourceIndex{number} 源工作表索引newName{string} 新工作表名称
复制工作表(含内容和格式)。
excel.copySheet(0, "备份");Excel.copySheet(sourceName, newName)
sourceName{string} 源工作表名称newName{string} 新工作表名称
根据名称复制工作表。
excel.copySheet("数据表", "数据表_副本");Excel.save([options])
options{Object} 保存配置(可选)path{string} 保存文件路径(可选,默认使用$excel.open()时指定的文件路径)password{string} 文件密码(可选,默认使用$excel.open()时指定的密码;仅支持.xlsx格式)
- 异常
{IOException} - 返回
{void}
保存工作簿到文件。
如果不传 options,则使用打开 Excel 时的文件路径和密码保存。
如果传入 options,可以指定新的保存路径或修改文件密码:
- 只传
path:另存为新文件,保持原密码 - 只传
password:保存到原文件,并修改密码 - 同时传
path和password:保存到指定文件,并使用新的密码
如果文件以只读模式打开,调用此方法会抛出异常。
注意
密码保护功能目前仅支持 .xlsx 文件格式。
var excel = $excel.open({
path: "/sdcard/data.xlsx"
});
var sheet = excel.sheet("Sheet1");
if (!sheet) sheet = excel.createSheet("Sheet1");
sheet.cell(0, 0).setValue("Hello Excel");
// 保存到原文件
excel.save();
console.log("💾 保存成功");var excel = $excel.open({
path: "/sdcard/data.xlsx"
});
var sheet = excel.sheet("Sheet1");
if (!sheet) sheet = excel.createSheet("Sheet1");
sheet.cell(0, 0).setValue("新的文件");
// 保存到新的路径
excel.save({
path: "/sdcard/data_copy.xlsx"
});
console.log("📄 已另存为 data_copy.xlsx");var excel = $excel.open({
path: "/sdcard/secret.xlsx",
password: "123456"
});
// 修改数据
var sheet = excel.sheet("数据");
if (!sheet) sheet = excel.createSheet("Sheet1");
sheet.cell(0, 0).setValue("修改后的内容");
// 使用新密码保存
excel.save({
password: "888888"
});
console.log("🔐 密码已修改");var excel = $excel.open({
path: "/sdcard/source.xlsx",
password: "123456"
});
// 修改内容
var sheet = excel.sheet("Sheet1");
if (!sheet) sheet = excel.createSheet("Sheet1");
sheet.cell(0, 0).setValue("新文件");
// 保存到新文件,并设置新密码
excel.save({
path: "/sdcard/new.xlsx",
password: "888888"
});
console.log("✅ 已生成新的密码文件");var excel = $excel.open({
path: "/sdcard/secret.xlsx",
password: "123456"
});
// 保存为普通 Excel 文件
excel.save({
path: "/sdcard/plain.xlsx",
password: null
});
console.log("🔓 已取消密码保护");Excel.readOnly()
- 返回 {boolean}
判断当前工作簿是否为只读模式。
if (excel.readOnly()) {
console.log("🔒 只读模式");
}Excel.xlsx()
- 返回 {boolean}
判断当前工作簿是否为 .xlsx 格式(XSSF)。
if (excel.xlsx()) {
console.log("📄 文件格式: .xlsx");
}Excel.xls()
- 返回 {boolean}
判断当前工作簿是否为 .xls 格式(HSSF)。
if (excel.xls()) {
console.log("📄 文件格式: .xls");
}Excel.close()
关闭工作簿,释放资源。
excel.close();
console.log("🔒 已关闭");ExcelSheet
由 Excel.sheet(name)、Excel.createSheet(name)、Excel.activeSheet() 等方法返回,代表一个工作表。
ExcelSheet.name()
- 返回 {string}
获取工作表名称。
console.log("名称:", sheet.name());ExcelSheet.setName(name)
name{string} 新名称
设置工作表名称。
sheet.setName("新名称");ExcelSheet.lastRow()
- 返回 {number}
获取最后一行的索引(从 0 开始)。
console.log("最后一行:", sheet.lastRow());ExcelSheet.lastColumn()
- 返回 {number}
获取最后一列的索引(从 0 开始)。
console.log("最后一列:", sheet.lastColumn());ExcelSheet.cell(address)
address{string} 单元格地址,如"A1"、"B2"- 返回 ExcelCell
通过地址获取单元格。
var cell = sheet.cell("A1");
cell.setValue("Hello");ExcelSheet.cell(row, col)
row{number} 行索引(从 0 开始)col{number} 列索引(从 0 开始)- 返回 ExcelCell
通过行列索引获取单元格。
var cell = sheet.cell(0, 0);
cell.setValue("Hello");ExcelSheet.range(address)
address{string} 范围地址,如"A1:B10"、"C5"- 返回 ExcelRange
通过地址获取范围。
var range = sheet.range("A1:C10");ExcelSheet.range(startRow, startCol, endRow, endCol)
startRow{number} 起始行索引startCol{number} 起始列索引endRow{number} 结束行索引endCol{number} 结束列索引- 返回 ExcelRange
通过行列索引获取范围。
var range = sheet.range(0, 0, 10, 2);ExcelSheet.range(row, col)
row{number} 行索引col{number} 列索引- 返回 ExcelRange
获取单个单元格的范围。
var cellRange = sheet.range(0, 0);ExcelSheet.row(index)
index{number} 行索引- 返回 ExcelRow
获取行对象。
var row = sheet.row(0);
console.log("行:", row.index());ExcelSheet.insertRow(rowIndex)
rowIndex{number} 插入位置(从 0 开始)- 返回 {boolean}
插入一行。
sheet.insertRow(1);ExcelSheet.deleteRow(rowIndex)
rowIndex{number} 要删除的行索引- 返回 {boolean}
删除一行。
sheet.deleteRow(1);ExcelSheet.insertColumn(colIndex)
colIndex{number} 插入位置(从 0 开始)- 返回 {ExcelSheet}
插入一列(链式调用)。
sheet.insertColumn(1);ExcelSheet.deleteColumn(colIndex)
colIndex{number} 要删除的列索引- 返回 {ExcelSheet}
删除一列(链式调用)。
sheet.deleteColumn(1);ExcelSheet.freeze(row, col)
row{number} 冻结的行数col{number} 冻结的列数- 返回 {ExcelSheet}
冻结窗格。
sheet.freeze(1, 0); // 冻结第1行
sheet.freeze(0, 1); // 冻结第A列
sheet.freeze(2, 1); // 冻结前2行和前1列ExcelSheet.unfreeze()
- 返回 {ExcelSheet}
取消冻结窗格。
sheet.unfreeze();ExcelSheet.autoFilter(range)
range{string} 筛选范围地址,如"A1:D100"- 返回 {ExcelSheet}
设置自动筛选。
sheet.autoFilter("A1:D100");ExcelSheet.hasAutoFilter()
- 返回 {boolean}
判断是否设置了自动筛选。
if (sheet.hasAutoFilter()) {
console.log("有自动筛选");
}ExcelSheet.autoFilterRange()
- 返回 {string} |
null
获取自动筛选的范围。
var range = sheet.autoFilterRange();
console.log("筛选范围:", range);ExcelSheet.removeFilter()
- 返回 {boolean}
移除自动筛选。
sheet.removeFilter();ExcelSheet.setProtect([password])
password{string} 可选,保护密码- 传入密码字符串:保护工作表(使用该密码)
- 不传参数或传入
null:取消保护
- 返回 {ExcelSheet}
// 使用密码保护
sheet.setProtect("password123");
// 取消保护
sheet.setProtect();
// 或
sheet.setProtect(null);ExcelSheet.protect()
- 返回 {boolean}
判断工作表是否被保护。
if (sheet.protect()) {
console.log("🔒 工作表已保护");
} else {
console.log("🔓 工作表未保护");
}// 检查是否已保护
if (sheet.protect()) {
console.log("已保护,先取消");
sheet.setProtect(); // 取消保护
}
// 设置密码保护
sheet.setProtect("password123");
console.log("是否保护:", sheet.protect()); // true
// 取消保护(两种方式等效)
sheet.setProtect(); // 取消保护
console.log("是否保护:", sheet.protect()); // false
// 再次设置密码保护
sheet.setProtect("abc");
console.log("是否保护:", sheet.protect()); // true
// 传入 null 取消保护
sheet.setProtect(null);
console.log("是否保护:", sheet.protect()); // falseExcelSheet.write(data[, startRow])
data{Array} 二维数组数据(行数组)startRow{number} 可选,起始行索引(从 0 开始)。若省略,则追加到工作表末尾(从最后一行之后开始写入)- 返回 {ExcelRange | null} 写入的数据区域,如果
data为空则返回null
写入数据到工作表。
- 省略
startRow时,数据追加到当前最后一行之后(新表从第 0 行开始)。 - 指定
startRow时,从该行开始覆盖写入(不会清除超出范围的行)。
var range = sheet.write([
["姓名", "年龄", "分数"],
["张三", 25, 90],
["李四", 30, 85]
]);
if (range) {
// 🔍 查看 range 信息
console.log("📍 地址:", range.address()); // A1:C2
console.log("📊 行数:", range.height()); // 2
console.log("📊 列数:", range.width()); // 3
console.log("📊 单元格数:", range.cellCount()); // 6
console.log("📋 值:", JSON.stringify(range.values(), null, 2));
// 🎨 设置样式
range.style().bold(true).background("#FFFF00");
}var range = sheet.write([
["产品", "价格"],
["苹果", 5.5]
], 3);
if (range) {
console.log("📍 写入区域:", range.address()); // A4:B5
console.log("📊 行数:", range.height()); // 2
console.log("📊 列数:", range.width()); // 2
console.log("📋 值:", JSON.stringify(range.values(), null, 2));
}ExcelSheet.read()
- 返回 {Array}
读取所有数据(二维数组)。
var data = sheet.read();
console.log(data);ExcelSheet.readRows()
- 返回 {Array<Object>}
将工作表数据读取为对象数组,每一行是一个对象,对象的键为列名。
- 如果工作表已设置表头(通过
ExcelSheet.createHeader(columns[, row])或ExcelSheet.setHeaderRow(row),则使用表头内容作为键名。 - 如果未设置表头,则自动生成列名:
A, B, C, ...(与 Excel 列字母一致)。
读取范围从 ExcelSheet.dataRow() 开始(有表头时自动跳过表头行,无表头时从第 0 行开始)。
sheet.setHeaderRow(0);//设置表头行
var rows = sheet.readRows();
for (var i = 0; i < rows.length; i++) {
console.log(rows[i].姓名, rows[i].年龄); // 有表头时,使用表头名称
// console.log(rows[i].A, rows[i].B); // 无表头时,第一行第一列
}ExcelSheet.createHeader(columns[, row])
columns{Array} 列名数组row{number} 可选,表头行索引(从 0 开始)。若省略,则使用当前表头行(默认第 0 行)- 返回 {ExcelSheet}
创建表头,并将工作表切换为“有表头”模式。
- 如果指定了
row,则在指定行创建表头,并将表头行设为该行。 - 如果省略
row,则在当前表头行创建表头。若从未设置表头行,则默认使用第 0 行。
// 使用当前表头行(默认为第 0 行)
sheet.createHeader(["姓名", "年龄", "分数"]);
// 在指定行(第 3 行)创建表头
sheet.createHeader(["产品", "价格", "库存"], 3);ExcelSheet.noHeader()
- 返回 {ExcelSheet}
切换为无表头模式。
sheet.noHeader();ExcelSheet.setHeaderRow(row)
row{number} 表头行索引(从 0 开始)。若传入-1,则切换为无表头模式- 返回
设置表头所在的行,并自动启用表头模式。
当 row 为 -1 时,将取消表头并切换为无表头模式,效果等同于调用 ExcelSheet.noHeader()。
// 设置表头在第 1 行
sheet.setHeaderRow(1);
// 取消表头(无表头模式)
sheet.setHeaderRow(-1);ExcelSheet.hasHeader()
- 返回 {boolean}
判断是否有表头。
if (sheet.hasHeader()) {
console.log("✅ 有表头");
}ExcelSheet.headerRow()
- 返回 {number}
获取表头行索引。
console.log("表头行:", sheet.headerRow());ExcelSheet.dataRow()
- 返回 {number}
获取数据区域的起始行索引。
- 有表头时:返回
headerRow + 1 - 无表头时:返回
0
// 第一行为表头
sheet.setHeaderRow(0);
console.log(sheet.dataRow()); // 1
// 无表头
sheet.noHeader();
console.log(sheet.dataRow()); // 0ExcelSheet.insert(values[, afterRow])
values{Object} 单行数据(Map)或多行数据(Array)afterRow{number} 可选,在哪个行号之后插入(从 0 开始)。若省略,则追加到工作表末尾- 返回 {ExcelRange | null} 插入的数据区域,如果未插入任何行则返回
null
在指定行之后插入数据,并返回新插入的数据区域。
- 省略
afterRow时,数据追加到当前最后一行之后(新表从第 0 行开始)。 - 指定
afterRow时,在该行之后插入数据(原有行自动下移)。
// 首次创建表头(位于第 3 行)
sheet.createHeader(["姓名", "年龄"], 3);
// 已有表头的 Excel,重新打开后只需指定表头行即可
// sheet.setHeaderRow(3);
var range = sheet.insert({ "姓名": "张三", "年龄": 18 });
if (range) {
// 🔍 查看 range 信息
console.log("📍 插入区域:", range.address()); // A1:B1
console.log("📊 行数:", range.height()); // 1
console.log("📊 列数:", range.width()); // 2
console.log("📋 值:", JSON.stringify(range.values(), null, 2));
// 🎨 设置样式
range.style().bold(true).background("#FFFF00");
}// 首次创建表头(位于第 3 行)
sheet.createHeader(["姓名", "年龄"], 3);
// 已有表头的 Excel,重新打开后只需指定表头行即可
// sheet.setHeaderRow(3);
var range = sheet.insert([
{ "姓名": "李四", "年龄": 19 },
{ "姓名": "王五", "年龄": 20 }
], 4);
if (range) {
console.log("📍 插入区域:", range.address()); // A2:B3
console.log("📊 行数:", range.height()); // 2
console.log("📊 列数:", range.width()); // 2
console.log("📊 单元格数:", range.cellCount()); // 4
console.log("📋 值:", JSON.stringify(range.values(), null, 2));
}// 切换为无表头模式(或直接不创建表头)
sheet.noHeader();
// 插入数据时,键名必须是 Excel 列字母(A, B, C...)
var range = sheet.insert({ "A": "产品", "B": "价格" });
if (range) {
console.log("📍 插入区域:", range.address()); // A1:B1
console.log("📋 值:", JSON.stringify(range.values(), null, 2));
}
// 批量插入多条
var range2 = sheet.insert([
{ "A": "苹果", "B": 5.5 },
{ "A": "香蕉", "B": 3.2 }
]);
if (range2) {
console.log("📍 插入区域:", range2.address()); // A2:B3(自动追加)
console.log("📊 行数:", range2.height()); // 2
}ExcelSheet.query([where])
where{string | Object} 可选 查询条件。若省略,则查询所有数据- 返回 {ExcelCursor}
执行查询,返回游标对象。游标提供了 count()、all()、single()、update()、delete() 等方法,用于操作结果集。
where 支持两种写法:字符串表达式 和 对象条件,均可组合多个条件。
字符串表达式(推荐用于简单/动态查询)
字符串条件使用类似 SQL 的语法,支持:
- 比较运算符:
>,<,>=,<=,=,==,!=,<>,LIKE - 逻辑运算符:
AND,OR(支持括号分组) - 字符串值需用单引号
'或双引号"包围 - 数字值无需引号
| 示例 | 说明 |
|---|---|
"年龄 > 20" | 年龄大于 20 |
"姓名 = '张三'" | 姓名等于“张三” |
"分数 >= 80 AND 班级 = '三班'" | 分数≥80 且 班级为“三班” |
"姓名 = '张三' OR 姓名 = '李四'" | 姓名等于“张三”或“李四” |
"(年龄 > 18 AND 班级 = '一班') OR (年龄 < 20 AND 班级 = '二班')" | 复合括号分组 |
"姓名 LIKE '张'" | 姓名包含“张”(不区分大小写) |
"分数 != 90" | 分数不等于 90 |
注意:
LIKE执行的是contains匹配(不区分大小写),而非通配符匹配。
对象条件(推荐用于精确/结构化查询)
对象条件以 JavaScript 对象形式编写,键为列名,值为比较值或运算符对象。
| 写法 | 说明 |
|---|---|
{ 姓名: "张三" } | 简写:默认 =,即姓名等于“张三” |
{ 年龄: { ">": 20 } } | 年龄大于 20 |
{ 分数: { ">=": 80 }, 班级: "三班" } | 多条件(默认 AND),分数≥80 且 班级为“三班” |
{ 姓名: { "!=": "张三" } } | 姓名不等于“张三” |
{ 年龄: { ">": 18, "<": 30 } } | 年龄在 18 到 30 之间(AND 关系) |
支持的操作符:>, <, >=, <=, =, ==, !=, <>, LIKE
注意:对象条件中的多个键之间是 AND 关系,不支持直接 OR,但可以通过字符串表达式实现 OR 逻辑。
操作符支持对照表
| 操作符 | 字符串写法 | 对象写法 | 说明 |
|---|---|---|---|
| 等于 | =, == | "=" 或省略(默认) | 值相等 |
| 不等于 | !=, <> | "!=" 或 "<>" | 值不等 |
| 大于 | > | ">" | 大于 |
| 大于等于 | >= | ">=" | 大于等于 |
| 小于 | < | "<" | 小于 |
| 小于等于 | <= | "<=" | 小于等于 |
| 模糊匹配 | LIKE(不区分大小写) | "LIKE" | 字符串包含(不区分大小写) |
混合使用场景
- 忽略
where:sheet.query()查询所有数据。 - 字符串 + 逻辑组合:
sheet.query("年龄 > 20 AND 班级 = '三班'") - 对象条件:
sheet.query({ 年龄: { ">": 20 }, 班级: "三班" }) - 对象条件 + LIKE:
sheet.query({ 姓名: { "LIKE": "张" } })
💡 提示:当列名包含特殊字符或空格时,建议使用对象条件,避免字符串解析歧义。
var excel = $excel.open({ path: "/sdcard/query_demo.xlsx" });
var sheet = excel.sheet("数据");
if (!sheet) sheet = excel.createSheet("数据");
// 1. 创建表头并插入测试数据
sheet.createHeader(["姓名", "年龄", "分数", "班级"]);
var insertRange = sheet.insert([
{ "姓名": "张三", "年龄": 18, "分数": 90, "班级": "一班" },
{ "姓名": "李四", "年龄": 19, "分数": 85, "班级": "二班" },
{ "姓名": "王五", "年龄": 20, "分数": 78, "班级": "一班" },
{ "姓名": "赵六", "年龄": 21, "分数": 92, "班级": "三班" },
{ "姓名": "孙七", "年龄": 19, "分数": 66, "班级": "二班" },
{ "姓名": "周八", "年龄": 22, "分数": 88, "班级": "三班" }
]);
if (insertRange) {
console.log("✅ 插入数据区域:", insertRange.address());
insertRange.style().border("thin").background("#F2F2F2");
}
// ========== 各种查询示例 ==========
// 2. 无条件查询(展示 range() 与 ranges() 的区别)
var cursor0 = sheet.query();
console.log("\n【无条件查询】总行数:", cursor0.count());
console.log("所有数据:", JSON.stringify(cursor0.all(), null, 2));
var range0 = cursor0.range();
console.log("📌 range() 连续范围:", range0.address(), "行数:", range0.height(), "列数:", range0.width());
var ranges0 = cursor0.ranges();
console.log("📌 ranges() 分组范围(共", ranges0.size(), "组):");
for (var i = 0; i < ranges0.size(); i++) {
var r = ranges0.get(i);
console.log(" 组" + (i+1) + ":", r.address(), "行数:", r.height(), "列数:", r.width());
}
// 3. 字符串条件:年龄 > 20
var cursor1 = sheet.query("年龄 > 20");
console.log("\n【字符串条件:年龄 > 20】行数:", cursor1.count());
console.log("结果:", JSON.stringify(cursor1.all(), null, 2));
// 4. 字符串条件:AND 组合
var cursor2 = sheet.query("年龄 > 20 AND 班级 = '三班'");
console.log("\n【字符串条件:年龄 > 20 AND 班级 = '三班'】行数:", cursor2.count());
console.log("结果:", JSON.stringify(cursor2.all(), null, 2));
// 5. 字符串条件:OR 组合(演示 ranges().style() 对不连续区域应用样式)
var cursor3 = sheet.query("班级 = '一班' OR 班级 = '三班'");
console.log("\n【字符串条件:班级 = '一班' OR 班级 = '三班'】行数:", cursor3.count());
console.log("结果:", JSON.stringify(cursor3.all(), null, 2));
var ranges3 = cursor3.ranges();
console.log("📌 ranges() 分组范围(共", ranges3.size(), "组):");
for (var j = 0; j < ranges3.size(); j++) {
var r = ranges3.get(j);
console.log(" 组" + (j+1) + ":", r.address());
}
// 对每个连续组分别设置样式(背景色+边框)
ranges3.style().border("thin").background("#E6F0FA");
// 6. 字符串条件:LIKE 模糊匹配
var cursor4 = sheet.query("姓名 LIKE '张'");
console.log("\n【字符串条件:姓名 LIKE '张'】行数:", cursor4.count());
console.log("结果:", JSON.stringify(cursor4.all(), null, 2));
// 7. 对象条件:分数 >= 80 且班级 = '三班'
var cursor5 = sheet.query({
分数: { ">=": 80 },
班级: "三班"
});
console.log("\n【对象条件:分数>=80 且 班级='三班'】行数:", cursor5.count());
console.log("结果:", JSON.stringify(cursor5.all(), null, 2));
// 8. 对象条件简写(默认 =)
var cursor6 = sheet.query({ 姓名: "张三" });
console.log("\n【对象条件简写:姓名='张三'】行数:", cursor6.count());
var single = cursor6.single();
console.log("张三的信息:", JSON.stringify(single, null, 2));
// 9. 对象条件:不等于
var cursor7 = sheet.query({ 班级: { "!=": "一班" } });
console.log("\n【对象条件:班级 != '一班'】行数:", cursor7.count());
console.log("结果:", JSON.stringify(cursor7.all(), null, 2));
// 10. 对象条件:范围(大于且小于)
var cursor8 = sheet.query({ 年龄: { ">": 18, "<": 22 } });
console.log("\n【对象条件:年龄 > 18 且 < 22】行数:", cursor8.count());
console.log("结果:", JSON.stringify(cursor8.all(), null, 2));
// ========== 更新与删除演示 ==========
var affected = sheet.query("班级 = '二班'").update({ "分数": 95 });
console.log("\n【更新】更新了", affected, "行");
var deleted = sheet.query("分数 < 70").delete();
console.log("【删除】删除了", deleted, "行");
// 游标遍历(查询年龄>=20)
var cursor9 = sheet.query("年龄 >= 20");
console.log("\n【游标遍历】年龄>=20 的行:");
while (cursor9.moveToNext()) {
var row = cursor9.pick();
console.log(" ", JSON.stringify(row, null, 2));
}
cursor9.close();
excel.save();
excel.close();var excel = $excel.open({ path: "/sdcard/query_noheader.xlsx" });
var sheet = excel.sheet("数据");
if (!sheet) sheet = excel.createSheet("数据");
// 1. 切换为无表头模式,插入数据(键名使用列字母 A, B, C...)
sheet.noHeader();
var insertRange1 = sheet.insert({ "A": "姓名", "B": "年龄", "C": "分数", "D": "班级" });
var insertRange2 = sheet.insert([
{ "A": "张三", "B": 18, "C": 90, "D": "一班" },
{ "A": "李四", "B": 19, "C": 85, "D": "二班" },
{ "A": "王五", "B": 20, "C": 78, "D": "一班" },
{ "A": "赵六", "B": 21, "C": 92, "D": "三班" },
{ "A": "孙七", "B": 19, "C": 66, "D": "二班" },
{ "A": "周八", "B": 22, "C": 88, "D": "三班" }
]);
if (insertRange2) {
console.log("✅ 插入数据区域:", insertRange2.address());
insertRange2.style().border("thin").background("#F2F2F2");
}
// ========== 各种查询示例(列名使用字母) ==========
// 2. 无条件查询(展示 range() 与 ranges() 的区别)
var cursor0 = sheet.query();
console.log("\n【无条件查询】总行数:", cursor0.count());
console.log("所有数据:", JSON.stringify(cursor0.all(), null, 2));
var range0 = cursor0.range();
console.log("📌 range() 连续范围:", range0.address(), "行数:", range0.height(), "列数:", range0.width());
var ranges0 = cursor0.ranges();
console.log("📌 ranges() 分组范围(共", ranges0.size(), "组):");
for (var i = 0; i < ranges0.size(); i++) {
var r = ranges0.get(i);
console.log(" 组" + (i+1) + ":", r.address(), "行数:", r.height(), "列数:", r.width());
}
// 3. 字符串条件:B > 20
var cursor1 = sheet.query("B > 20");
console.log("\n【字符串条件:B > 20】行数:", cursor1.count());
console.log("结果:", JSON.stringify(cursor1.all(), null, 2));
// 4. 字符串条件:AND 组合
var cursor2 = sheet.query("B > 20 AND D = '三班'");
console.log("\n【字符串条件:B > 20 AND D = '三班'】行数:", cursor2.count());
console.log("结果:", JSON.stringify(cursor2.all(), null, 2));
// 5. 字符串条件:OR 组合(演示 ranges().style() 对不连续区域应用样式)
var cursor3 = sheet.query("D = '一班' OR D = '三班'");
console.log("\n【字符串条件:D = '一班' OR D = '三班'】行数:", cursor3.count());
console.log("结果:", JSON.stringify(cursor3.all(), null, 2));
var ranges3 = cursor3.ranges();
console.log("📌 ranges() 分组范围(共", ranges3.size(), "组):");
for (var j = 0; j < ranges3.size(); j++) {
var r = ranges3.get(j);
console.log(" 组" + (j+1) + ":", r.address());
}
ranges3.style().border("thin").background("#E6F0FA");
// 6. LIKE 模糊匹配
var cursor4 = sheet.query("A LIKE '张'");
console.log("\n【字符串条件:A LIKE '张'】行数:", cursor4.count());
console.log("结果:", JSON.stringify(cursor4.all(), null, 2));
// 7. 对象条件:C>=80 且 D='三班'
var cursor5 = sheet.query({
C: { ">=": 80 },
D: "三班"
});
console.log("\n【对象条件:C>=80 且 D='三班'】行数:", cursor5.count());
console.log("结果:", JSON.stringify(cursor5.all(), null, 2));
// 8. 对象条件简写
var cursor6 = sheet.query({ A: "张三" });
console.log("\n【对象条件简写:A='张三'】行数:", cursor6.count());
var single = cursor6.single();
console.log("张三的信息:", JSON.stringify(single, null, 2));
// 9. 对象条件:不等于
var cursor7 = sheet.query({ D: { "!=": "一班" } });
console.log("\n【对象条件:D != '一班'】行数:", cursor7.count());
console.log("结果:", JSON.stringify(cursor7.all(), null, 2));
// 10. 对象条件:范围
var cursor8 = sheet.query({ B: { ">": 18, "<": 22 } });
console.log("\n【对象条件:B > 18 且 B < 22】行数:", cursor8.count());
console.log("结果:", JSON.stringify(cursor8.all(), null, 2));
// ========== 更新与删除 ==========
var affected = sheet.query("D = '二班'").update({ C: 95 });
console.log("\n【更新】更新了", affected, "行");
var deleted = sheet.query("C < 70").delete();
console.log("【删除】删除了", deleted, "行");
excel.save();
excel.close();ExcelSheet.mergedRegions()
获取所有合并区域。
// 1. 创建一些合并区域
sheet.range("A1:B2").merge();
sheet.range("D4:E5").merge();
// 2. 获取并打印所有合并区域
var merged = sheet.mergedRegions();
console.log("合并区域数:", merged.size());
for (var i = 0; i < merged.size(); i++) {
var range = merged.get(i);
console.log(" 区域" + (i+1) + ":", range.formatAsString());
}ExcelSheet.validations()
获取所有数据验证。
var validations = sheet.validations();
console.log("数据验证数:", validations.size());ExcelSheet.setColumnWidth(column, charWidth)
column{number | string} 列索引(从 0 开始)或列字母(如"A"、"B")charWidth{number} 字符宽度- 返回 {ExcelSheet}
设置列宽。支持通过列索引(数字)或列字母(字符串)指定列。
// 通过列索引设置
sheet.setColumnWidth(0, 15);
// 通过列字母设置
sheet.setColumnWidth("A", 15);ExcelSheet.setColumnWidth(columns, charWidth)
columns{Array} 列标识数组,元素可以是数字索引或列字母(如["A","C","E"]或[0,2,4])charWidth{number} 字符宽度- 返回 {ExcelSheet}
为多个指定列统一设置相同的宽度。
sheet.setColumnWidth(["A", "C", "E"], 15);
sheet.setColumnWidth([0, 2, 4], 15);ExcelSheet.columnWidth(column)
column{number | string} 列索引或列字母- 返回 {number} 列宽(字符宽度)
获取指定列的宽度。
var w1 = sheet.columnWidth(0);
var w2 = sheet.columnWidth("A");
console.log("A列宽度:", w1);ExcelSheet.columnWidthExact(column)
column{number | string} 列索引或列字母- 返回 {number} 列宽(1/256 字符宽度单位,POI 内部单位)
获取指定列的精确宽度(以 1/256 字符为单位)。
var exact = sheet.columnWidthExact("A");ExcelSheet.setColumnHidden(column, hidden)
column{number | string} 列索引或列字母hidden{boolean}true隐藏,false显示- 返回 {ExcelSheet}
设置列的隐藏状态。
sheet.setColumnHidden(2, true); // 隐藏 C 列
sheet.setColumnHidden("C", true); // 同上ExcelSheet.columnHidden(column)
column{number | string} 列索引或列字母- 返回 {boolean}
true表示隐藏,false表示显示
判断指定列是否隐藏。
if (sheet.columnHidden(2)) {
console.log("C 列已隐藏");
}ExcelSheet.columns()
- 返回 {Array<string>}
获取列名列表。
var colNames = sheet.columns();
console.log("列名:", colNames);ExcelSheet.clear()
-->清空整个 Sheet(删除所有内容、样式、合并区域、图片等)。
sheet.clear();ExcelSheet.clearContents()
-->仅清空内容(保留样式)。
sheet.clearContents();ExcelSheet.clearStyles()
-->仅清空样式(保留内容)。
sheet.clearStyles();ExcelSheet.clearMerges()
-->清空所有合并区域。
sheet.clearMerges();ExcelSheet.clearComments()
-->清空所有批注。
sheet.clearComments();ExcelSheet.clearImages()
-->清空所有图片。
sheet.clearImages();ExcelSheet.poiSheet()
获取底层 POI Sheet 对象,用于执行 POI 原生操作(如高级格式设置、公式、图形等)。
var poiSheet = sheet.poiSheet();
// 例如:获取 Sheet 的保护状态
var isProtected = poiSheet.getProtect();ExcelCursor
由 ExcelSheet.query([where]) 返回,代表查询结果集。
完整测试示例
// ==================== ExcelCursor 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelCursorTest/
var TEST_DIR = "/sdcard/ExcelCursorTest/";
files.ensureDir(TEST_DIR);
// 打开(或创建)工作簿,并获取 Sheet
var excel = $excel.open({ path: TEST_DIR + "cursor_test.xlsx" });
var sheetName = "Cursor测试";
if (excel.hasSheet(sheetName)) {
excel.removeSheet(sheetName);
}
var sheet = excel.createSheet(sheetName);
console.log("\n📦 准备测试数据...");
// 1. 创建表头并插入数据(有表头模式)
sheet.createHeader(["姓名", "年龄", "分数", "班级"]);
sheet.insert([
{ "姓名": "张三", "年龄": 18, "分数": 90, "班级": "一班" },
{ "姓名": "李四", "年龄": 19, "分数": 85, "班级": "二班" },
{ "姓名": "王五", "年龄": 20, "分数": 78, "班级": "一班" },
{ "姓名": "赵六", "年龄": 21, "分数": 92, "班级": "三班" },
{ "姓名": "孙七", "年龄": 19, "分数": 66, "班级": "二班" },
{ "姓名": "周八", "年龄": 22, "分数": 88, "班级": "三班" }
]);
excel.save({ path: TEST_DIR + "cursor_base.xlsx" });
console.log("基础数据已保存 -> cursor_base.xlsx");
// 辅助函数:打印分隔线
function sep(title) {
console.log("\n========== " + title + " ==========");
}
// ==================== 1. 无条件查询 ====================
sep("1. 无条件查询 (query())");
var cursor = sheet.query();
console.log("count():", cursor.count());
console.log("empty():", cursor.empty());
console.log("nonEmpty():", cursor.nonEmpty());
console.log("all():", JSON.stringify(cursor.all(), null, 2));
// 测试 single()
var singleRow = cursor.single();
console.log("single():", JSON.stringify(singleRow, null, 2));
// 测试 rows() / rowIndexes() / columns()
var rows = cursor.rows();
console.log("rows().size():", rows.size());
var indexes = cursor.rowIndexes();
console.log("rowIndexes():", JSON.stringify(indexes));
var cols = cursor.columns();
console.log("columns():", JSON.stringify(cols));
// 测试 range() 和 ranges()
var rangeAll = cursor.range();
console.log("range().address():", rangeAll.address());
var ranges = cursor.ranges();
console.log("ranges().size():", ranges.size());
// ==================== 2. 条件查询(字符串) ====================
sep("2. 条件查询 (字符串)");
var cursor2 = sheet.query("年龄 > 20");
console.log("年龄 > 20 的行数:", cursor2.count());
console.log("结果:", JSON.stringify(cursor2.all(), null, 2));
// 测试游标遍历
console.log("遍历结果:");
while (cursor2.moveToNext()) {
var row = cursor2.pick();
console.log(" ", JSON.stringify(row));
// 测试 row() / cells()
var excelRow = cursor2.row();
console.log(" ExcelRow.index():", excelRow.index());
var cells = cursor2.cells();
console.log(" cells().size():", cells.size());
}
cursor2.reset();
console.log("reset() 后 position():", cursor2.position());
// ==================== 3. 条件查询(对象) ====================
sep("3. 条件查询 (对象)");
var cursor3 = sheet.query({
分数: { ">=": 80 },
班级: "三班"
});
console.log("分数>=80 且 班级='三班' 的行数:", cursor3.count());
console.log("结果:", JSON.stringify(cursor3.all(), null, 2));
// ==================== 4. 更新操作 ====================
sep("4. 更新操作 (update)");
var cursor4 = sheet.query("班级 = '二班'");
var updated = cursor4.update({ "班级": "火箭班" });
console.log("更新了", updated, "行");
// 验证更新结果
var cursor4_check = sheet.query("班级 = '火箭班'");
console.log("更新后火箭班人数:", cursor4_check.count());
// ==================== 5. 删除操作 ====================
sep("5. 删除操作 (delete)");
var cursor5 = sheet.query("分数 < 70");
var deleted = cursor5.delete(); // 不压缩
console.log("删除了", deleted, "行(不压缩空行)");
// 验证
var cursor5_check = sheet.query("分数 < 70");
console.log("剩余分数<70的行数:", cursor5_check.count());
// 删除后插入一条新数据,然后测试带压缩的删除
sheet.insert({ "姓名": "临时", "年龄": 30, "分数": 50, "班级": "测试" });
var cursor6 = sheet.query("姓名 = '临时'");
var deleted2 = cursor6.delete(true); // 压缩空行
console.log("删除了", deleted2, "行(压缩空行)");
// ==================== 6. 样式操作 ====================
sep("6. 样式操作 (style)");
var cursor7 = sheet.query("班级 = '一班'");
cursor7.style()
.bold(true)
.background("#FFFF00")
.fontColor("#FF0000");
console.log("一班数据已应用样式(请打开文件查看)");
// ==================== 7. 空结果测试 ====================
sep("7. 空结果测试");
var cursor8 = sheet.query("姓名 = '不存在'");
console.log("empty():", cursor8.empty());
console.log("nonEmpty():", cursor8.nonEmpty());
console.log("count():", cursor8.count());
console.log("single():", cursor8.single());
console.log("range().address():", cursor8.range().address()); // 应返回空范围 A0:A0
console.log("ranges().size():", cursor8.ranges().size());
// ==================== 8. position() 测试 ====================
sep("8. position() 测试");
var cursor9 = sheet.query("年龄 >= 19");
console.log("初始 position():", cursor9.position());
var i = 0;
while (cursor9.moveToNext()) {
console.log("第", i, "次 moveToNext 后 position():", cursor9.position());
i++;
}
cursor9.reset();
console.log("reset() 后 position():", cursor9.position());
cursor9.close();
console.log("close() 后 position():", cursor9.position());
// ==================== 9. 无表头模式测试 ====================
sep("9. 无表头模式测试");
// 新建一个 Sheet 并切换到无表头模式
var sheetNoHeader = excel.createSheet("无表头测试");
sheetNoHeader.noHeader();
// 插入数据(键名为列字母)
sheetNoHeader.insert({ "A": "姓名", "B": "年龄", "C": "分数", "D": "班级" });
sheetNoHeader.insert([
{ "A": "张三", "B": 18, "C": 90, "D": "一班" },
{ "A": "李四", "B": 19, "C": 85, "D": "二班" }
]);
var cursorNo = sheetNoHeader.query("B > 18");
console.log("无表头查询结果:", JSON.stringify(cursorNo.all(), null, 2));
console.log("columns():", JSON.stringify(cursorNo.columns()));
// ==================== 10. 综合测试:rows() 和 range() 结合 ====================
sep("10. rows() 与 range() 结合");
var cursor10 = sheet.query("年龄 >= 20");
var rowColl = cursor10.rows();
console.log("rows().size():", rowColl.size());
rowColl.forEach(function(row) {
console.log("行索引:", row.index());
});
var range10 = cursor10.range();
console.log("range().address():", range10.address());
// 保存最终结果
excel.save({ path: TEST_DIR + "cursor_final.xlsx" });
console.log("\n✅ 所有测试完成!");
console.log("📁 生成的文件位于:", TEST_DIR);
console.log(" 基础文件: cursor_base.xlsx");
console.log(" 最终文件: cursor_final.xlsx");
excel.close();ExcelCursor.moveToNext()
- 返回 {boolean}
移动到下一条记录。
while (cursor.moveToNext()) {
var row = cursor.pick();
console.log(row);
}ExcelCursor.pick()
- 返回 {Object | null}
获取当前行的 Object。
var row = cursor.pick();ExcelCursor.row()
- 返回 {ExcelRow | null}
获取当前行的 ExcelRow 对象。
var row = cursor.row();ExcelCursor.cells()
- 返回 {ExcelCellCollection}
获取当前行的单元格集合。
var cells = cursor.cells();ExcelCursor.all()
- 返回 {Array}
获取所有数据。
var all = cursor.all();ExcelCursor.single()
- 返回 {Object | null}
获取第一条数据。
var first = cursor.single();ExcelCursor.count()
- 返回 {number}
获取行数。
console.log("行数:", cursor.count());ExcelCursor.empty()
- 返回 {boolean}
判断是否为空。
if (cursor.empty()) {
console.log("没有匹配的数据");
}ExcelCursor.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (cursor.nonEmpty()) {
console.log("有匹配的数据");
}ExcelCursor.update(values)
values{Object} 要更新的数据- 返回 {number} 更新的行数
更新匹配的行。
var updated = cursor.update({
状态: "已处理",
处理时间: new Date()
});
console.log("更新了", updated, "行");ExcelCursor.delete([compress])
compress{boolean} 可选,是否压缩空行(删除后上移后续行),默认false- 返回 {number} 删除的行数
删除所有匹配行。若 compress 为 true,删除后会将后续行上移填补空位(注意:可能影响公式、合并区域等,请谨慎使用)。
// 不压缩空行(默认)
var deleted = cursor.delete();
console.log("删除了", deleted, "行");
// 删除并压缩空行
var deleted = cursor.delete(true);
console.log("删除了", deleted, "行");ExcelCursor.ranges()
- 返回 {ExcelRangeCollection}
将匹配行按连续块分组,每个分组内不含非匹配行,返回 ExcelRangeCollection(集合中的每个 ExcelRange 对应一个连续块)。
var ranges = cursor.ranges();
for (var i = 0; i < ranges.size(); i++) {
var range = ranges.get(i);
console.log("块" + (i+1) + ":", range.address());
}ExcelCursor.range()
- 返回 {ExcelRange}
返回一个连续的矩形范围,从第一个匹配行的起始列到最后一个匹配行的结束列,中间可能包含不符合查询条件的行。
var range = cursor.range();
console.log("连续范围:", range.address());ExcelCursor.rows()
- 返回 {ExcelRowCollection}
获取匹配的行集合。
var rows = cursor.rows();ExcelCursor.rowIndexes()
- 返回 {Array}
获取行索引数组。
var indexes = cursor.rowIndexes();ExcelCursor.columns()
- 返回 {Array}
获取列名数组。
var columns = cursor.columns();ExcelCursor.style()
- 返回 {ExcelStyle}
为匹配行设置样式。
cursor.style()
.bold(true)
.background("#FFFF00");ExcelCursor.position()
- 返回 {number}
获取当前位置。
console.log("位置:", cursor.position());ExcelCursor.close()
关闭游标。
cursor.close();ExcelRange
由 ExcelSheet.range(address)、ExcelSheet.range(startRow, startCol, endRow, endCol) 等方法返回,代表一个单元格区域。
完整测试示例
// ==================== ExcelRange 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelRangeTest/
var TEST_DIR = "/sdcard/ExcelRangeTest/";
files.ensureDir(TEST_DIR);
// 打开(或创建)工作簿,并获取 Sheet
var excel = $excel.open({ path: TEST_DIR + "range_test_base.xlsx" });
var sheet = excel.sheet("Range测试");
if (!sheet) sheet = excel.createSheet("Range测试");
// 清空可能存在的旧数据(保留 Sheet)
// sheet.clear();
// ==================== 准备基础数据 ====================
console.log("\n📦 准备基础数据...");
sheet.range("A1:E10").setValue([
["姓名", "语文", "数学", "英语", "总分"],
["张三", 85, 90, 88, 263],
["李四", 78, 82, 80, 240],
["王五", 92, 85, 90, 267],
["赵六", 70, 75, 72, 217],
["孙七", 88, 92, 85, 265],
["周八", 95, 88, 93, 276],
["吴九", 65, 70, 68, 203],
["郑十", 80, 85, 82, 247],
["合计", "=SUM(B2:B9)", "=SUM(C2:C9)", "=SUM(D2:D9)", "=SUM(E2:E9)"]
]);
excel.save({ path: TEST_DIR + "00_base.xlsx" });
console.log("基础数据已保存 -> 00_base.xlsx");
// ==================== 1. 基础属性 ====================
console.log("\n🔍 1. 基础属性测试");
var r1 = sheet.range("B2:D4");
console.log(" address() :", r1.address());
console.log(" height() :", r1.height());
console.log(" width() :", r1.width());
console.log(" firstRow() :", r1.firstRow());
console.log(" lastRow() :", r1.lastRow());
console.log(" firstColumn() :", r1.firstColumn());
console.log(" lastColumn() :", r1.lastColumn());
console.log(" cellCount() :", r1.cellCount());
console.log(" empty() :", r1.empty());
// ==================== 2. 读写数据 ====================
console.log("\n✏️ 2. 读写数据测试");
var r2 = sheet.range("A12:C14");
r2.setValue([
["产品", "单价", "数量"],
["苹果", 5.5, 10],
["香蕉", 3.2, 15]
]);
console.log(" setValue(二维数组) 后,value() 返回:");
console.log(" " + JSON.stringify(r2.value()));
var r3 = sheet.range("E12");
r3.setValue("备注");
console.log(" setValue(单值) 后,value() =", r3.value());
// ==================== 3. 单元格访问 ====================
console.log("\n📍 3. 单元格访问测试");
var r4 = sheet.range("A15:D17");
r4.setValue([
["A", "B", "C", "D"],
["E", "F", "G", "H"],
["I", "J", "K", "L"]
]);
console.log(" firstCell() :", r4.firstCell().value());
console.log(" lastCell() :", r4.lastCell().value());
console.log(" cell(1,2) :", r4.cell(1, 2).value());
// ==================== 4. 包含关系判断 ====================
console.log("\n🔎 4. 包含关系测试");
var r5 = sheet.range("B2:E9");
console.log(" range: " + r5.address());
console.log(" contains(2,2) :", r5.contains(2, 2));
console.log(" contains('C3') :", r5.contains("C3"));
var cell = sheet.cell("D4");
console.log(" contains(cell) :", r5.contains(cell));
var sub = sheet.range("C3:D4");
console.log(" contains(sub) :", r5.contains(sub));
var out = sheet.range("F1:G2");
console.log(" contains(out) :", r5.contains(out));
// ==================== 5. 复制与粘贴 ====================
console.log("\n📋 5. 复制与粘贴测试");
var src = sheet.range("A1:E1");
var tgt = sheet.range("G1:K1");
console.log(" 源地址:", src.address(), "目标地址:", tgt.address());
src.copy(tgt);
console.log(" copy() 后目标值:", JSON.stringify(tgt.value()));
var tgtVal = sheet.range("G3:K3");
src.paste(tgtVal);
console.log(" paste() 后目标值:", JSON.stringify(tgtVal.value()));
src.style().bold(true).background("#FFCC00");
src.pasteStyle(tgt);
console.log(" pasteStyle() 后样式已应用(请打开文件查看)");
// ==================== 6. 范围调整 ====================
console.log("\n🔄 6. 偏移与调整大小测试");
var r6 = sheet.range("A20:B21");
console.log(" 原始地址:", r6.address());
var offset = r6.offset(2, 3);
console.log(" offset(2,3) :", offset.address());
var resize = r6.resize(4, 5);
console.log(" resize(4,5) :", resize.address());
// ==================== 7. 清空操作 ====================
console.log("\n🧹 7. 清空测试");
var r7a = sheet.range("A23:C25");
r7a.setValue([
["a", "b", "c"],
["d", "e", "f"],
["g", "h", "i"]
]);
console.log(" 写入数据:", JSON.stringify(r7a.value()));
r7a.clearContents();
console.log(" clearContents() 后:", JSON.stringify(r7a.value()));
var r7b = sheet.range("E23:G25");
r7b.setValue([
["x", "y", "z"],
["1", "2", "3"],
["4", "5", "6"]
]);
r7b.style().bold(true).background("#FF0000").fontColor("#FFFFFF");
r7b.clearStyles();
console.log(" clearStyles() 后样式已重置(请打开文件查看)");
// ==================== 8. 合并操作 ====================
console.log("\n🧩 8. 合并测试");
var r8a = sheet.range("A28:B29");
console.log(" 即将合并:", r8a.address());
var idx = r8a.merge();
console.log(" merge() 返回索引:", idx);
console.log(" isMerged():", r8a.isMerged());
var r8b = sheet.range("D28:E29");
r8b.merge();
console.log(" 第二个合并区域:", r8b.address());
var r8c = sheet.range("C28:D28");
console.log(" 检查重叠:", r8c.address(), "hasMergedOverlap() =", r8c.hasMergedOverlap());
if (r8c.hasMergedOverlap()) {
r8c.clearMergedOverlap();
console.log(" 已清除重叠");
}
r8a.unmerge();
console.log(" unmerge() 后 isMerged():", r8a.isMerged());
// ==================== 9. 排序 ====================
console.log("\n📊 9. 排序测试");
var r9 = sheet.range("A30:E38");
r9.setValue([
["姓名", "班级", "分数", "排名", "备注"],
["张三", "一班", 85, "", ""],
["李四", "二班", 92, "", ""],
["王五", "一班", 78, "", ""],
["赵六", "三班", 95, "", ""],
["孙七", "二班", 88, "", ""],
["周八", "一班", 70, "", ""],
["吴九", "三班", 82, "", ""],
["郑十", "二班", 90, "", ""]
]);
console.log(" 排序前数据:", JSON.stringify(r9.value()));
r9.sort(2, false);
console.log(" 按分数降序后数据:", JSON.stringify(r9.value()));
r9.sort([1, 2], [true, false]);
console.log(" 按班级升序+分数降序后数据:", JSON.stringify(r9.value()));
// ==================== 10. 集合操作 ====================
console.log("\n🔗 10. 交集与并集测试");
var r10a = sheet.range("A40:C45");
var r10b = sheet.range("B41:D42");
console.log(" 范围1:", r10a.address());
console.log(" 范围2:", r10b.address());
var inter = r10a.intersect(r10b);
console.log(" intersect():", inter ? inter.address() : "无交集");
var uni = r10a.union(r10b);
console.log(" union():", uni ? uni.address() : "不相邻,无法并集");
// ==================== 11. 样式 ====================
console.log("\n🎨 11. 样式测试");
var r11 = sheet.range("A47:C49");
r11.setValue([
["样式", "演示", "区域"],
["加粗", "斜体", "下划线"],
["颜色", "背景", "边框"]
]);
r11.style()
.bold(true)
.italic(true)
.underline(true)
.fontColor("#FF0000")
.background("#FFFF00")
.border("thin")
.align("center")
.vertical("middle");
console.log(" 样式已应用(请打开文件查看效果)");
// ==================== 12. 批注 ====================
console.log("\n💬 12. 批注测试");
var r12 = sheet.range("A51:B52");
r12.setValue([
["批注1", "批注2"],
["注释", "说明"]
]);
r12.setComment("这是整个范围的批注", "测试员");
console.log(" setComment() 后,comments().size() =", r12.comments().size());
var cmt = r12.comment(0, 0);
if (cmt) {
console.log(" comment(0,0) 内容:", cmt.text(), "作者:", cmt.author());
}
r12.removeComment();
console.log(" removeComment() 后,comments().size() =", r12.comments().size());
// ==================== 13. 超链接 ====================
console.log("\n🔗 13. 超链接测试");
var r13 = sheet.range("A54:C55");
r13.setValue([
["链接1", "链接2", "链接3"],
["邮件", "文件", "网址"]
]);
r13.setHyperlink("https://example.com");
r13.setEmailHyperlink("user@example.com");
r13.setFileHyperlink("/sdcard/test.txt");
console.log(" setHyperlink() 后 hyperlinks().size() =", r13.hyperlinks().size());
var links = r13.hyperlinks();
links.forEach(function(h) {
console.log("链接: 地址=" + h.address() + ", 标签=" + h.label() + ", 类型=" + h.type());
});
r13.removeHyperlink();
console.log(" removeHyperlink() 后 hyperlinks().size() =", r13.hyperlinks().size());
// ==================== 14. 底层对象 ====================
console.log("\n⚙️ 14. 底层对象测试");
var r14 = sheet.range("A57:C58");
console.log(" poiSheet().getSheetName() =", r14.poiSheet().getSheetName());
var addr = r14.poiCellRangeAddress();
console.log(" poiCellRangeAddress().formatAsString() =", addr.formatAsString());
console.log(" poiCellRangeAddress().getFirstRow() =", addr.getFirstRow());
// ==================== 15. 额外测试 ====================
console.log("\n📌 15. 单格测试");
var r15 = sheet.range("A60");
r15.setValue("单格测试");
console.log(" 单格 cellCount =", r15.cellCount());
console.log(" 单格 empty =", r15.empty());
console.log(" 单格 value =", r15.value());
// ==================== 完成 ====================
excel.save({ path: TEST_DIR + "99_final.xlsx" });
console.log("\n✅ 所有测试完成!");
console.log("📁 生成的文件位于:", TEST_DIR);
console.log(" 基础文件: 00_base.xlsx");
console.log(" 最终文件: 99_final.xlsx(包含所有修改)");
excel.close();ExcelRange.height()
- 返回 {number}
获取高度(行数)。
console.log("高度:", range.height());ExcelRange.width()
- 返回 {number}
获取宽度(列数)。
console.log("宽度:", range.width());ExcelRange.firstRow()
- 返回 {number}
获取首行索引。
console.log("首行:", range.firstRow());ExcelRange.lastRow()
- 返回 {number}
获取末行索引。
console.log("末行:", range.lastRow());ExcelRange.firstColumn()
- 返回 {number}
获取首列索引。
console.log("首列:", range.firstColumn());ExcelRange.lastColumn()
- 返回 {number}
获取末列索引。
console.log("末列:", range.lastColumn());ExcelRange.cellCount()
- 返回 {number}
获取单元格总数。
console.log("单元格数:", range.cellCount());ExcelRange.empty()
- 返回 {boolean}
判断是否为空(所有单元格均为空)。
if (range.empty()) {
console.log("该范围为空");
}ExcelRange.address()
- 返回 {string}
获取范围地址。
console.log("地址:", range.address());ExcelRange.setValue(value)
value{Object | Array} 要设置的值- 返回 {ExcelRange}
设置值。
// 所有单元格设为 100
range.setValue(100);
// 设置二维数组
range.setValue([
["A", "B", "C"],
[1, 2, 3],
[4, 5, 6]
]);ExcelRange.value()
- 返回 {Object | Array}
获取值(单格返回值,多格返回二维数组)。
var val = range.value();ExcelRange.contains(row, col)
row{number} 行索引col{number} 列索引- 返回 {boolean}
判断坐标是否在范围内。
console.log(range.contains(2, 2));ExcelRange.contains(address)
address{string} 单元格地址- 返回 {boolean}
判断地址是否在范围内。
console.log(range.contains("D2"));ExcelRange.contains(cell)
cell{ExcelCell} 单元格对象- 返回 {boolean}
判断单元格是否在范围内。
var cell = sheet.cell(2, 2);
console.log(range.contains(cell));ExcelRange.contains(range)
range{ExcelRange} 范围对象- 返回 {boolean}
判断另一个范围是否在范围内。
var subRange = sheet.range("B2:C3");
console.log(range.contains(subRange));ExcelRange.firstCell()
- 返回 ExcelCell
获取第一个单元格。
var first = range.firstCell();ExcelRange.lastCell()
- 返回 ExcelCell
获取最后一个单元格。
var last = range.lastCell();ExcelRange.cell(rowOffset, colOffset)
rowOffset{number} 行偏移colOffset{number} 列偏移- 返回 ExcelCell
通过偏移获取单元格(相对于范围左上角)。
var cell = range.cell(1, 0); // 第2行,第1列ExcelRange.copy(target)
target{ExcelRange} 目标范围- 返回 {ExcelRange}
完整复制(值 + 样式)。
var source = sheet.range("A1:C10");
var target = sheet.range("E1:G10");
source.copy(target);ExcelRange.copy(targetAddress)
targetAddress{string} 目标地址- 返回 {ExcelRange}
复制到地址字符串。
source.copy("E1:G10");ExcelRange.paste(target)
target{ExcelRange} 目标范围- 返回 {ExcelRange}
仅粘贴值到范围。
source.paste(target);ExcelRange.paste(targetAddress)
targetAddress{string} 目标地址- 返回 {ExcelRange}
粘贴到地址字符串。
source.paste("E1:G10");ExcelRange.pasteStyle(target)
target{ExcelRange} 目标范围- 返回 {ExcelRange}
仅粘贴样式。
source.pasteStyle(target);ExcelRange.pasteStyle(targetAddress)
targetAddress{string} 目标地址- 返回 {ExcelRange}
粘贴样式到地址字符串。
source.pasteStyle("E1:G10");ExcelRange.offset(rowOffset, colOffset)
rowOffset{number} 行偏移colOffset{number} 列偏移- 返回 {ExcelRange}
偏移范围。
var newRange = range.offset(2, 1);ExcelRange.resize(newRows, newCols)
newRows{number} 新行数newCols{number} 新列数- 返回 {ExcelRange}
调整范围大小。
var resized = range.resize(3, 2);ExcelRange.merge()
- 返回 {number} 合并区域的索引
- 异常 {IllegalStateException} 如果存在重叠合并区域
合并单元格。
range.merge();ExcelRange.unmerge()
- 返回 {ExcelRange}
取消合并。
range.unmerge();ExcelRange.hasMergedOverlap()
- 返回 {boolean}
检查是否有重叠的合并区域。
if (range.hasMergedOverlap()) {
range.clearMergedOverlap();
}ExcelRange.clearMergedOverlap()
清除重叠的合并区域。
range.clearMergedOverlap();ExcelRange.isMerged()
- 返回 {boolean}
判断是否为合并区域。
if (range.isMerged()) {
console.log("该范围是合并区域");
}ExcelRange.sort(column, ascending)
column{number | string} 排序列索引(从 0 开始)或列字母(如"B")ascending{boolean} 是否升序- 返回 {ExcelRange}
单列排序(默认有标题行)。
// 按列索引排序
range.sort(1, true); // 升序
range.sort(1, false); // 降序
// 按列字母排序
range.sort("B", true);ExcelRange.sort(column, ascending, hasHeader)
column{number} 排序列索引ascending{boolean} 是否升序hasHeader{boolean} 是否有标题行- 返回 {ExcelRange}
单列排序(指定标题行)。
range.sort(1, true, false); // 无标题行ExcelRange.sort(columns, ascending)
columns{number[]} 排序列索引数组ascending{boolean[]} 升降序数组- 返回 {ExcelRange}
多列排序。
range.sort([1, 2], [true, false]);ExcelRange.sort(columns, ascending, hasHeader)
columns{number[]} 排序列索引数组ascending{boolean[]} 升降序数组hasHeader{boolean} 是否有标题行- 返回 {ExcelRange}
多列排序(指定标题行)。
range.sort([1, 2], [true, false], true);ExcelRange.intersect(other)
other{ExcelRange} 另一个范围- 返回 {ExcelRange} |
null
获取交集。
var range1 = sheet.range("A1:C10");
var range2 = sheet.range("B2:D5");
var intersect = range1.intersect(range2);ExcelRange.union(other)
other{ExcelRange} 另一个范围- 返回 {ExcelRange} |
null
获取并集(需要相邻)。
var union = range1.union(range2);ExcelRange.style()
- 返回 ExcelStyle
获取样式构建器。
range.style()
.bold(true)
.fontColor("#FFFFFF")
.background("#4472C4");ExcelRange.setComment(text[, author])
text{string} 批注内容author{string} 可选,作者名称- 返回 {ExcelRange}
添加批注。若指定 author,则同时设置作者信息。
注意
如果目标单元格已有批注,再次调用此方法会抛出 IllegalStateException(POI 不允许一个单元格有多个批注)。建议在设置前先调用 removeComment() 移除旧批注,或使用 comments() 检查是否存在。
// 安全做法:先移除旧批注,再设置新批注
range.removeComment();
console.log("已移除旧批注");
range.setComment("这是批注内容", "作者名");
console.log("已设置新批注,作者:", "作者名");
// 或者检查后再设置
if (range.comments().size() > 0) {
range.removeComment();
console.log("检测到已有批注,已移除");
}
range.setComment("这是批注内容");
console.log("已设置批注内容");ExcelRange.removeComment()
- 返回 {ExcelRange}
删除批注。
range.removeComment();ExcelRange.comments()
获取批注集合。
var comments = range.comments();ExcelRange.comment(rowOffset, colOffset)
rowOffset{number} 行偏移colOffset{number} 列偏移- 返回 {ExcelComment} |
null
获取指定位置的批注。
var comment = range.comment(0, 0);ExcelRange.setHyperlink(url)
url{string} 超链接地址- 返回 {ExcelRange}
添加超链接。
range.setHyperlink("https://example.com");ExcelRange.setEmailHyperlink(email)
email{string} 邮箱地址- 返回 {ExcelRange}
添加邮箱超链接。
range.setEmailHyperlink("user@example.com");ExcelRange.setFileHyperlink(path)
path{string} 文件路径- 返回 {ExcelRange}
添加文件超链接。
range.setFileHyperlink("/sdcard/file.pdf");ExcelRange.removeHyperlink()
- 返回 {ExcelRange}
删除超链接。
range.removeHyperlink();ExcelRange.hyperlinks()
获取超链接集合。
var links = range.hyperlinks();ExcelRange.hasAnyHyperlink()
- 返回 {boolean}
检查是否有超链接。
if (range.hasAnyHyperlink()) {
console.log("存在超链接");
}ExcelRange.clear()
- 返回 {ExcelRange}
清空所有(内容和样式)。
range.clear();ExcelRange.clearContents()
- 返回 {ExcelRange}
仅清空内容。
range.clearContents();ExcelRange.clearStyles()
- 返回 {ExcelRange}
仅清空样式。
range.clearStyles();ExcelRange.poiSheet()
获取底层 POI Sheet 对象,用于执行 POI 原生工作表操作(如行/列操作、合并区域等)。
var poiSheet = range.poiSheet();ExcelRange.poiCellRangeAddress()
获取底层 POI CellRangeAddress 对象,用于执行 POI 原生范围操作。
var address = range.poiCellRangeAddress();ExcelRangeCollection
由 ExcelCursor.ranges() 返回,代表多个不连续范围的集合。
完整测试示例
// ==================== ExcelRangeCollection 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelRangeCollectionTest/
var TEST_DIR = "/sdcard/ExcelRangeCollectionTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({ path: TEST_DIR + "range_collection_test.xlsx" });
var sheet = excel.sheet("测试");
if (!sheet) sheet = excel.createSheet("测试");
// 1. 创建表头并插入测试数据(使匹配行不连续)
sheet.createHeader(["姓名", "年龄", "班级"]);
sheet.insert([
{ "姓名": "张三", "年龄": 18, "班级": "一班" },
{ "姓名": "李四", "年龄": 19, "班级": "二班" },
{ "姓名": "王五", "年龄": 20, "班级": "一班" },
{ "姓名": "赵六", "年龄": 21, "班级": "三班" },
{ "姓名": "孙七", "年龄": 22, "班级": "二班" },
{ "姓名": "周八", "年龄": 23, "班级": "三班" }
]);
// 2. 查询条件产生不连续的匹配行(一班和三班)
// 匹配行索引:0, 2, 3, 5 → 连续块:[0], [2-3], [5] 共3块
var cursor = sheet.query("班级 = '一班' OR 班级 = '三班'");
var ranges = cursor.ranges();
console.log("\n========== ExcelRangeCollection 测试 ==========");
// 3. 测试 size()
console.log("size():", ranges.size()); // 预期 3
// 4. 测试 empty() 和 nonEmpty()
console.log("empty():", ranges.empty()); // false
console.log("nonEmpty():", ranges.nonEmpty()); // true
// 5. 测试 cellCount()(每行3列,总行数 = 各块行数之和)
console.log("cellCount():", ranges.cellCount()); // 预期 12
// 6. 测试 get(index)
console.log("\n--- get(index) ---");
console.log("get(0).address():", ranges.get(0).address());
console.log("get(1).address():", ranges.get(1).address());
console.log("get(2).address():", ranges.get(2).address());
// 7. 测试 forEach(callback)
console.log("\n--- forEach 遍历 ---");
ranges.forEach(function(range) {
console.log("范围:", range.address());
});
// 8. 测试 filter(predicate)
console.log("\n--- filter ---");
var filtered = ranges.filter(function(range) {
return range.height() > 1; // 使用 height() 获取行数
});
console.log("过滤后 size:", filtered.size());
// 使用 size() + get() 遍历过滤后的结果
console.log("过滤后范围(使用 size/get 遍历):");
for (var i = 0; i < filtered.size(); i++) {
var range = filtered.get(i);
console.log(" 范围 " + i + ":", range.address());
}
// 9. 测试 find(predicate)
console.log("\n--- find ---");
var found = ranges.find(function(range) {
// 查找第一个范围(直接返回 true 即可)
return true;
});
console.log("找到的范围地址:", found ? found.address() : "未找到");
// 10. 测试 style()
console.log("\n--- style ---");
ranges.style()
.bold(true)
.background("#FFFF00")
.border("thin");
console.log("样式已应用到所有范围(请打开文件查看效果)");
// 保存最终文件
excel.save({ path: TEST_DIR + "range_collection_final.xlsx" });
console.log("\n✅ 测试完成!");
console.log("📁 生成的文件:", TEST_DIR + "range_collection_final.xlsx");
excel.close();ExcelRangeCollection.size()
- 返回 {number}
获取范围数量。
console.log("范围数:", ranges.size());ExcelRangeCollection.empty()
- 返回 {boolean}
判断是否为空。
if (ranges.empty()) {
console.log("没有范围");
}ExcelRangeCollection.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (ranges.nonEmpty()) {
console.log("有范围");
}ExcelRangeCollection.cellCount()
- 返回 {number}
获取单元格总数。
console.log("单元格总数:", ranges.cellCount());ExcelRangeCollection.get(index)
index{number} 索引- 返回 {ExcelRange} |
null
获取指定索引的范围。
// 使用 size() 和 get() 遍历
for (var i = 0; i < ranges.size(); i++) {
var r = ranges.get(i);
console.log(r.address());
}ExcelRangeCollection.forEach(callback)
callback{Function} 回调函数(range) => void- 返回 {ExcelRangeCollection}
遍历所有范围。
ranges.forEach(function(range) {
console.log(range.address());
});ExcelRangeCollection.filter(predicate)
predicate{Function} 过滤函数(range) => boolean- 返回 {ExcelRangeCollection}
过滤范围。
var largeRanges = ranges.filter(function(range) {
return range.cellCount() > 10;
});ExcelRangeCollection.find(predicate)
predicate{Function} 过滤函数(range) => boolean- 返回 {ExcelRange} |
null
查找第一个匹配的范围。
var found = ranges.find(function(range) {
return range.address() === "A1:B2";
});ExcelRangeCollection.style()
- 返回 ExcelStyle
为所有范围设置样式。
ranges.style()
.bold(true)
.background("#FFFF00")
.border("thin");ExcelRow
由 ExcelSheet.row(index) 返回,代表一行。
完整测试示例
// ==================== ExcelRow 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelRowTest/
var TEST_DIR = "/sdcard/ExcelRowTest/";
files.ensureDir(TEST_DIR);
// 打开(或创建)工作簿,并获取 Sheet
var excel = $excel.open({ path: TEST_DIR + "row_test.xlsx" });
var sheetName = "Row测试";
if (excel.hasSheet(sheetName)) {
excel.removeSheet(sheetName);
}
var sheet = excel.createSheet(sheetName);
// ==================== 准备基础数据 ====================
console.log("\n📦 准备基础数据...");
sheet.range("A1:E5").setValue([
["姓名", "语文", "数学", "英语", "总分"],
["张三", 85, 90, 88, 263],
["李四", 78, 82, 80, 240],
["王五", 92, 85, 90, 267],
["赵六", 70, 75, 72, 217]
]);
sheet.range("A7:E7").setValue(["", "", "", "", ""]);
excel.save({ path: TEST_DIR + "row_base.xlsx" });
console.log("基础数据已保存 -> row_base.xlsx");
// ==================== 获取行对象 ====================
var row0 = sheet.row(0);
var row1 = sheet.row(1);
var row2 = sheet.row(2);
var row3 = sheet.row(3);
var row4 = sheet.row(4);
var row7 = sheet.row(7);
console.log("\n========== 1. 基本属性测试 ==========");
console.log("row0.index():", row0.index());
console.log("row0.empty():", row0.empty());
console.log("row0.cellCount():", row0.cellCount());
console.log("row1.cellCount():", row1.cellCount());
console.log("row1.empty():", row1.empty());
// ==================== 2. 单元格操作 ====================
console.log("\n========== 2. 单元格操作测试 ==========");
console.log("row1.cell(0).value():", row1.cell(0).value());
console.log("row1.cell(4).value():", row1.cell(4).value());
var cells = row1.cells();
console.log("row1.cells().size():", cells.size());
console.log("row1.cells() 第一个值:", cells.size() > 0 ? cells.get(0).value() : "无");
var testRow = sheet.row(10);
testRow.setValue(["测试", 100, 200, 300, 400]);
console.log("testRow.value():", JSON.stringify(testRow.value()));
// ==================== 3. 行高操作 ====================
console.log("\n========== 3. 行高操作测试 ==========");
console.log("row1.height():", row1.height());
row1.setHeight(30);
console.log("设置后 row1.height():", row1.height());
row1.autoHeight();
console.log("autoHeight() 后 row1.height():", row1.height());
// ==================== 4. 样式操作(链式调用) ====================
console.log("\n========== 4. 样式操作测试(链式) ==========");
// 给 row1 设置样式:加粗、红色字体、黄色背景、边框、居中
row1.style()
.bold(true)
.fontColor("#FF0000")
.background("#FFFF00")
.border("thin")
.align("center")
.vertical("middle");
console.log("row1 样式已应用(请打开文件查看)");
// ==================== 5. 复制与粘贴 ====================
console.log("\n========== 5. 复制与粘贴测试 ==========");
var targetRow = sheet.row(12);
console.log("复制前 targetRow 值:", JSON.stringify(targetRow.value()));
row1.copy(targetRow);
console.log("copy() 后 targetRow 值:", JSON.stringify(targetRow.value()));
var pasteTarget = sheet.row(14);
pasteTarget.setValue(["旧值", "旧值", "旧值", "旧值", "旧值"]);
console.log("粘贴前 pasteTarget 值:", JSON.stringify(pasteTarget.value()));
row1.paste(pasteTarget);
console.log("paste() 后 pasteTarget 值:", JSON.stringify(pasteTarget.value()));
// 粘贴样式到另一行
var styleTarget = sheet.row(16);
styleTarget.setValue(["样式", "粘贴", "测试"]);
row1.pasteStyle(styleTarget);
console.log("pasteStyle() 后样式已应用(请打开文件查看)");
// 测试索引版本
row1.copy(18);
console.log("copy(18) 后第18行值:", JSON.stringify(sheet.row(18).value()));
var pasteIndexTarget = sheet.row(20);
pasteIndexTarget.setValue(["P1", "P2", "P3", "P4", "P5"]);
row1.paste(20);
console.log("paste(20) 后第20行值:", JSON.stringify(sheet.row(20).value()));
var pasteStyleIndexTarget = sheet.row(22);
pasteStyleIndexTarget.setValue(["S1", "S2", "S3"]);
row1.pasteStyle(22);
console.log("pasteStyle(22) 后样式已应用");
// ==================== 6. 行删除与插入 ====================
console.log("\n========== 6. 行删除与插入测试 ==========");
var insertRow = sheet.row(24);
insertRow.setValue(["插入前", "数据"]);
console.log("插入前第24行值:", JSON.stringify(insertRow.value()));
insertRow.insert();
console.log("insert() 后第24行值:", JSON.stringify(sheet.row(24).value()));
console.log("第25行值:", JSON.stringify(sheet.row(25).value()));
var deleteRow = sheet.row(26);
deleteRow.setValue(["删除行", "测试"]);
console.log("删除前第26行值:", JSON.stringify(deleteRow.value()));
deleteRow.remove();
console.log("remove() 后第26行值:", JSON.stringify(sheet.row(26).value()));
// ==================== 7. 隐藏操作 ====================
console.log("\n========== 7. 隐藏操作测试 ==========");
var hideRow = sheet.row(28);
hideRow.setValue(["隐藏行", "测试"]);
console.log("隐藏前 hidden():", hideRow.hidden());
hideRow.setHidden(true);
console.log("setHidden(true) 后 hidden():", hideRow.hidden());
// ==================== 8. poiRow 和 getSheet ====================
console.log("\n========== 8. 底层对象测试 ==========");
console.log("poiRow().getRowNum():", row1.poiRow().getRowNum());
// ==================== 9. cells() 集合操作 ====================
console.log("\n========== 9. cells() 集合操作测试 ==========");
var cellColl = row1.cells();
console.log("cellColl.size():", cellColl.size());
var testRow2 = sheet.row(30);
testRow2.setValue(["A", "B", "C", "D", "E"]);
var cells2 = testRow2.cells();
cells2.forEach(function(cell) {
console.log("单元格值:", cell.value());
});
cells2.style().bold(true).background("#CCCCCC");
console.log("批量样式已应用");
// ==================== 完成 ====================
excel.save({ path: TEST_DIR + "row_final.xlsx" });
console.log("\n✅ 所有测试完成!");
console.log("📁 生成的文件位于:", TEST_DIR);
console.log(" 基础文件: row_base.xlsx");
console.log(" 最终文件: row_final.xlsx(包含所有修改)");
excel.close();ExcelRow.index()
- 返回 {number}
获取行索引。
console.log("行:", row.index());ExcelRow.empty()
- 返回 {boolean}
判断行是否为空(所有单元格均为空)。
if (row.empty()) {
console.log("该行为空");
}ExcelRow.cellCount()
- 返回 {number}
获取行中的单元格数量(包括空白单元格,即最大列索引 + 1)。若行中没有单元格,返回 -1。
console.log("单元格数:", row.cellCount());ExcelRow.cell(index)
index{number} 列索引- 返回 ExcelCell
获取或创建指定列的单元格。如果单元格不存在,会自动创建。
var cell = row.cell(0);ExcelRow.cells()
获取当前行所有已存在的单元格(不含空白)。
var cells = row.cells();
console.log("单元格数:", cells.size());ExcelRow.setValue(values)
values{Array} 值数组- 返回 {ExcelRow}
设置行中所有单元格的值。如果数组长度超过现有列数,会自动创建新列。
row.setValue(["A", "B", "C"]);ExcelRow.value()
- 返回
获取行中所有单元格的值(按列顺序)。
var values = row.value();
console.log(JSON.stringify(values));ExcelRow.setHeight(heightInPoints)
heightInPoints{number} 行高(单位:点)- 返回 {ExcelRow}
设置行高(单位:点)。
row.setHeight(20);ExcelRow.height()
- 返回 {number}
获取行高(单位:点)。
console.log("行高:", row.height());ExcelRow.autoHeight()
- 返回 {ExcelRow}
自动调整行高(根据内容)。
row.autoHeight();ExcelRow.style()
- 返回 ExcelStyle
获取行样式构建器,支持链式设置字体、颜色、背景、边框、对齐等样式。
// 链式设置样式
row.style()
.bold(true)
.fontColor("#FF0000")
.background("#FFFF00")
.border("thin")
.align("center");ExcelRow.copy(target)
将当前行的值和样式完整复制到目标行(包括行高、隐藏状态)。
row.copy(targetRow);ExcelRow.copy(targetIndex)
targetIndex{number} 目标行索引- 返回 {ExcelRow}
将当前行的值和样式完整复制到指定索引的目标行(不存在则创建)。
row.copy(5);ExcelRow.paste(target)
将当前行的值粘贴到目标行(仅值,不改变样式和行属性)。
row.paste(targetRow);ExcelRow.paste(targetIndex)
targetIndex{number} 目标行索引- 返回 {ExcelRow}
将当前行的值粘贴到指定索引的目标行(不存在则创建)。
row.paste(5);ExcelRow.pasteStyle(target)
将当前行的样式粘贴到目标行(仅样式,不改变值和行属性)。
row.pasteStyle(targetRow);ExcelRow.pasteStyle(targetIndex)
targetIndex{number} 目标行索引- 返回 {ExcelRow}
将当前行的样式粘贴到指定索引的目标行(不存在则创建)。
row.pasteStyle(5);ExcelRow.remove()
从工作表中删除当前行。
row.remove();ExcelRow.insert()
在当前行前插入一个空白行,并将当前行及其下方所有行下移。插入后,当前 ExcelRow 对象会更新为指向新插入的行。
row.insert();ExcelRow.setHidden(hidden)
hidden{boolean} 是否隐藏- 返回 {ExcelRow}
设置行的隐藏状态。
row.setHidden(true);ExcelRow.hidden()
- 返回 {boolean}
判断行是否隐藏。
if (row.hidden()) {
console.log("行已隐藏");
}ExcelRow.poiRow()
- 返回 {org.apache.poi.ss.usermodel.Row}
获取底层 POI Row 对象,用于执行 POI 原生行操作。
var poiRow = row.poiRow();ExcelRowCollection
由 ExcelCursor.rows() 等方法返回,代表行的集合。
ExcelRowCollection.size()
- 返回 {number}
获取行数。
console.log("行数:", rows.size());ExcelRowCollection.empty()
- 返回 {boolean}
判断是否为空。
if (rows.empty()) {
console.log("没有行");
}ExcelRowCollection.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (rows.nonEmpty()) {
console.log("有行");
}ExcelRowCollection.get(index)
index{number} 索引- 返回 {ExcelRow | null}
获取指定索引的行。
var row = rows.get(0);
console.log(row.index());ExcelRowCollection.forEach(callback)
callback{Function} 回调函数(row) => void- 返回 {ExcelRowCollection}
遍历所有行。
rows.forEach(function(row) {
console.log("行索引:", row.index());
});
// 使用 size() + get() 遍历
for (var i = 0; i < rows.size(); i++) {
var row = rows.get(i);
console.log("行索引:", row.index());
}ExcelRowCollection.filter(filter)
filter{Function} 过滤函数(row) => boolean- 返回 {ExcelRowCollection}
过滤行。
var nonEmptyRows = rows.filter(function(row) {
return !row.empty();
});ExcelRowCollection.find(filter)
filter{Function} 过滤函数(row) => boolean- 返回 {ExcelRow | null}
查找第一个匹配的行。
var found = rows.find(function(row) {
return row.index() === 5;
});ExcelCell
由 ExcelSheet.cell(address)、ExcelRange.cell(rowOffset, colOffset) 等方法返回,代表一个单元格。
完整测试示例
// ==================== ExcelCell 完整 API 测试(严谨类型处理) ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelCellTest/
var TEST_DIR = "/sdcard/ExcelCellTest/";
files.ensureDir(TEST_DIR);
// 打开(或创建)工作簿,并获取 Sheet
var excel = $excel.open({ path: TEST_DIR + "cell_test.xlsx" });
var sheetName = "Cell测试";
if (excel.hasSheet(sheetName)) {
excel.removeSheet(sheetName);
}
var sheet = excel.createSheet(sheetName);
// ==================== 准备基础数据 ====================
console.log("\n📦 准备基础数据...");
sheet.range("A1:E5").setValue([
["姓名", "年龄", "分数", "日期", "是否通过"],
["张三", 25, 90.5, "2024-01-15", true],
["李四", 30, 85.0, "2024-02-20", false],
["王五", 22, 76.5, "2024-03-10", true],
["赵六", 28, 92.0, "2024-04-05", false]
]);
excel.save({ path: TEST_DIR + "cell_base.xlsx" });
console.log("基础数据已保存 -> cell_base.xlsx");
// ==================== 获取单元格对象 ====================
var cellA1 = sheet.cell("A1"); // 字符串
var cellB2 = sheet.cell(1, 1); // 数字:25
var cellC3 = sheet.cell("C3"); // 数字:76.5
var cellD4 = sheet.cell(3, 3); // 文本日期
var cellE5 = sheet.cell("E5"); // 布尔值:false
var cellF1 = sheet.cell(0, 5); // 空单元格
console.log("\n========== 1. 值读写测试 ==========");
console.log("cellA1.value():", cellA1.value(), "类型:", cellA1.type());
console.log("cellB2.value():", cellB2.value(), "类型:", cellB2.type());
console.log("cellC3.value():", cellC3.value(), "类型:", cellC3.type());
console.log("cellD4.value():", cellD4.value(), "类型:", cellD4.type());
console.log("cellE5.value():", cellE5.value(), "类型:", cellE5.type());
// 修改值
cellA1.setValue("新表头");
console.log("setValue('新表头') 后 cellA1.value():", cellA1.value());
// 类型安全的取值方式
if (cellA1.type() === "STRING") {
console.log("cellA1.string():", cellA1.string());
} else {
console.log("cellA1 不是字符串类型,无法调用 string()");
}
if (cellB2.type() === "NUMERIC") {
console.log("cellB2.number():", cellB2.number());
} else {
console.log("cellB2 不是数字类型,无法调用 number()");
}
if (cellE5.type() === "BOOLEAN") {
console.log("cellE5.bool():", cellE5.bool());
} else {
console.log("cellE5 不是布尔类型,无法调用 bool()");
}
// 如果类型不确定,使用 value() 更安全
console.log("cellC3.value():", cellC3.value());
// ==================== 2. 类型判断测试 ====================
console.log("\n========== 2. 类型判断测试 ==========");
console.log("cellA1.type():", cellA1.type());
console.log("cellB2.type():", cellB2.type());
console.log("cellF1.empty():", cellF1.empty());
console.log("cellA1.empty():", cellA1.empty());
console.log("cellB2.isMerged():", cellB2.isMerged()); // 尚未合并
// ==================== 3. 样式测试 ====================
console.log("\n========== 3. 样式测试 ==========");
var styleCell = sheet.cell("A10");
styleCell.setValue("样式测试");
styleCell.style()
.bold(true)
.fontColor("#FF0000")
.background("#FFFF00")
.border("thin")
.align("center");
console.log("样式已应用到 A10");
// ==================== 4. 复制与粘贴测试 ====================
console.log("\n========== 4. 复制与粘贴测试 ==========");
var srcCell = sheet.cell("B2"); // 25
var dstCell = sheet.cell("G1");
console.log("源值:", srcCell.value(), "目标值:", dstCell.value());
srcCell.copy(dstCell);
console.log("copy() 后目标值:", dstCell.value());
var pasteTarget = sheet.cell("H1");
pasteTarget.setValue("旧值");
srcCell.paste(pasteTarget);
console.log("paste() 后目标值:", pasteTarget.value());
var styleTarget = sheet.cell("I1");
styleTarget.setValue("样式粘贴测试");
srcCell.pasteStyle(styleTarget);
console.log("pasteStyle() 后样式已应用(请打开文件查看)");
// 地址版本
srcCell.copy("G2");
console.log("copy('G2') 后 G2 值:", sheet.cell("G2").value());
srcCell.paste("H2");
console.log("paste('H2') 后 H2 值:", sheet.cell("H2").value());
srcCell.pasteStyle("I2");
console.log("pasteStyle('I2') 后样式已应用");
// ==================== 5. 超链接测试 ====================
console.log("\n========== 5. 超链接测试 ==========");
var linkCell = sheet.cell("A20");
linkCell.setValue("点击访问");
linkCell.setHyperlink("https://example.com");
console.log("hasHyperlink():", linkCell.hasHyperlink());
var link = linkCell.hyperlink();
if (link) {
console.log("超链接地址:", link.address());
console.log("超链接类型:", link.type());
}
linkCell.setEmailHyperlink("user@example.com");
console.log("setEmailHyperlink 后地址:", linkCell.hyperlink().address());
linkCell.removeHyperlink();
console.log("removeHyperlink() 后 hasHyperlink():", linkCell.hasHyperlink());
// ==================== 6. 批注测试 ====================
console.log("\n========== 6. 批注测试 ==========");
var commentCell = sheet.cell("B20");
commentCell.setValue("批注单元格");
commentCell.setComment("这是一个批注");
console.log("hasComment():", commentCell.hasComment());
var cmt = commentCell.comment();
if (cmt) {
console.log("批注内容:", cmt.text());
console.log("批注作者:", cmt.author());
}
commentCell.removeComment();
console.log("removeComment() 后 hasComment():", commentCell.hasComment());
// ==================== 7. 数据验证测试 ====================
console.log("\n========== 7. 数据验证测试 ==========");
var dropCell = sheet.cell("C20");
dropCell.setValue("性别");
dropCell.setDropdown(["男", "女", "其他"]);
console.log("下拉列表验证已添加");
var intCell = sheet.cell("D20");
intCell.setValue("年龄");
intCell.setInteger({ min: 18, max: 60, promptTitle: "年龄输入", promptText: "请输入18-60", errorTitle: "错误", errorText: "年龄范围18-60" });
console.log("整数验证已添加");
var decCell = sheet.cell("E20");
decCell.setValue("分数");
decCell.setDecimal({ min: 0, max: 100 });
console.log("小数验证已添加");
var dateCell = sheet.cell("F20");
dateCell.setValue("日期");
dateCell.setDate({ min: "2020-01-01", max: "2025-12-31" });
console.log("日期验证已添加");
var timeCell = sheet.cell("G20");
timeCell.setValue("时间");
timeCell.setTime({ min: "08:00", max: "18:00" });
console.log("时间验证已添加");
var textCell = sheet.cell("H20");
textCell.setValue("文本长度");
textCell.setTextLength({ minLength: 5, maxLength: 10 });
console.log("文本长度验证已添加");
// 删除验证
textCell.removeValidation();
console.log("removeValidation() 后验证已删除");
// ==================== 8. 公式测试 ====================
console.log("\n========== 8. 公式测试 ==========");
var formulaCell = sheet.cell("A30");
formulaCell.setFormula("SUM(B2:B5)");
console.log("formula():", formulaCell.formula());
console.log("hasFormula():", formulaCell.hasFormula());
console.log("calculatedValue():", formulaCell.calculatedValue());
// ==================== 9. 格式化文本测试 ====================
console.log("\n========== 9. 格式化文本测试 ==========");
var dateCell2 = sheet.cell("D4");
dateCell2.style().dateFormat("yyyy-MM-dd");
console.log("text():", dateCell2.text());
var evaluator = excel.poiWorkbook().getCreationHelper().createFormulaEvaluator();
console.log("text(evaluator):", dateCell2.text(evaluator));
// ==================== 10. 图片插入测试 ====================
console.log("\n========== 10. 图片插入测试 ==========");
var imgPath = "/sdcard/drawnImg.png";
if (files.isFile(imgPath)) {
var imgCell = sheet.cell("A40");
imgCell.setValue("图片");
imgCell.setImage(imgPath)
.size(200, 150)
.insert();
console.log("图片已插入到 A40");
} else {
console.log("⚠️ 测试图片不存在,跳过图片插入测试");
}
// ==================== 11. poiCell 测试 ====================
console.log("\n========== 11. poiCell 底层对象测试 ==========");
var poiCell = cellB2.poiCell();
console.log("poiCell.getColumnIndex():", poiCell.getColumnIndex());
console.log("poiCell.getRowIndex():", poiCell.getRowIndex());
// ==================== 12. 其他方法:type、empty、isMerged 已在前面覆盖 ====================
console.log("\n✅ 所有测试完成!");
// ==================== 保存 ====================
excel.save({ path: TEST_DIR + "cell_final.xlsx" });
console.log("📁 生成的文件位于:", TEST_DIR);
console.log(" 基础文件: cell_base.xlsx");
console.log(" 最终文件: cell_final.xlsx(包含所有修改)");
excel.close();ExcelCell.poiCell()
获取底层 POI Cell 对象。
var poiCell = cell.poiCell();ExcelCell.value()
- 返回 {Object}
获取单元格值。
console.log("值:", cell.value());ExcelCell.setValue(value)
value{Object} 要设置的值- 返回 {ExcelCell}
设置单元格值。
cell.setValue("Hello");ExcelCell.type()
- 返回 {string}
获取单元格类型(如 STRING、NUMERIC、BOOLEAN、FORMULA、BLANK 等)。
console.log("类型:", cell.type());ExcelCell.string()
- 返回 {string}
仅当单元格类型为 STRING 时有效,否则抛出 IllegalStateException。
建议先使用 type() 判断类型,或使用通用的 value() 方法。
// 安全调用
if (cell.type() === "STRING") {
console.log("字符串:", cell.string());
} else {
console.log("单元格不是字符串类型,当前类型:", cell.type());
}
// 或者使用通用方法
console.log("值:", cell.value());ExcelCell.number()
- 返回 {number}
仅当单元格类型为 NUMERIC 时有效,否则抛出 IllegalStateException。
建议先使用 type() 判断类型,或使用通用的 value() 方法。
// 安全调用
if (cell.type() === "NUMERIC") {
console.log("数字:", cell.number());
} else {
console.log("单元格不是数值类型,当前类型:", cell.type());
}
// 或者使用通用方法
console.log("值:", cell.value());ExcelCell.bool()
- 返回 {boolean}
仅当单元格类型为 BOOLEAN 时有效,否则抛出 IllegalStateException。
建议先使用 type() 判断类型,或使用通用的 value() 方法。
// 安全调用
if (cell.type() === "BOOLEAN") {
console.log("布尔值:", cell.bool());
} else {
console.log("单元格不是布尔类型,当前类型:", cell.type());
}
// 或者使用通用方法
console.log("值:", cell.value());ExcelCell.empty()
- 返回 {boolean}
判断单元格是否为空(内容为空或单元格为空白)。
if (cell.empty()) {
console.log("单元格为空");
}ExcelCell.isMerged()
- 返回 {boolean}
判断是否为合并区域的一部分。
if (cell.isMerged()) {
console.log("该单元格位于合并区域");
}ExcelCell.style()
- 返回 ExcelStyle
获取样式构建器,支持链式设置字体、颜色、背景、边框、对齐等样式。
cell.style()
.bold(true)
.fontColor("#FF0000")
.background("#FFFF00");ExcelCell.copy(target)
将当前单元格的值和样式完整复制到目标单元格。
cell.copy(targetCell);ExcelCell.copy(targetAddress)
targetAddress{string} 目标单元格地址(如"B5")- 返回 {ExcelCell}
将当前单元格的值和样式完整复制到指定地址。
cell.copy("B5");ExcelCell.paste(target)
仅将当前单元格的值粘贴到目标单元格(不改变目标样式)。
cell.paste(targetCell);ExcelCell.paste(targetAddress)
targetAddress{string} 目标单元格地址(如"B5")- 返回 {ExcelCell}
仅将当前单元格的值粘贴到指定地址。
cell.paste("B5");ExcelCell.pasteStyle(target)
仅将当前单元格的样式粘贴到目标单元格(不改变目标值)。
cell.pasteStyle(targetCell);ExcelCell.pasteStyle(targetAddress)
targetAddress{string} 目标单元格地址(如"B5")- 返回 {ExcelCell}
仅将当前单元格的样式粘贴到指定地址。
cell.pasteStyle("B5");ExcelCell.setHyperlink(url)
url{string} 超链接地址- 返回 {ExcelCell}
添加超链接。
cell.setHyperlink("https://example.com");ExcelCell.setEmailHyperlink(email)
email{string} 邮箱地址- 返回 {ExcelCell}
添加邮箱超链接(自动添加 mailto: 前缀)。
cell.setEmailHyperlink("user@example.com");ExcelCell.setFileHyperlink(path)
path{string} 文件路径- 返回 {ExcelCell}
添加文件超链接。
cell.setFileHyperlink("/sdcard/file.pdf");ExcelCell.removeHyperlink()
- 返回 {ExcelCell}
删除超链接。
cell.removeHyperlink();ExcelCell.hasHyperlink()
- 返回 {boolean}
判断是否有超链接。
if (cell.hasHyperlink()) {
console.log("有超链接");
}ExcelCell.hyperlink()
- 返回 {ExcelHyperlink | null}
获取超链接信息对象。
var link = cell.hyperlink();
if (link) {
console.log("地址:", link.address());
console.log("类型:", link.type());
}ExcelCell.setComment(text)
text{string} 批注内容- 返回 {ExcelCell}
添加批注(如果已存在批注,新批注会覆盖旧批注)。
cell.setComment("这是批注内容");ExcelCell.removeComment()
- 返回 {ExcelCell}
删除批注。
cell.removeComment();ExcelCell.hasComment()
- 返回 {boolean}
判断是否有批注。
if (cell.hasComment()) {
console.log("有批注");
}ExcelCell.comment()
- 返回 {ExcelComment | null}
获取批注信息对象。
var comment = cell.comment();
if (comment) {
console.log("内容:", comment.text());
console.log("作者:", comment.author());
}ExcelCell.setDropdown(options)
options{Array} 下拉选项列表- 返回 {ExcelCell}
添加下拉列表验证(自动处理选项长度超限问题)。
cell.setDropdown(["男", "女", "其他"]);ExcelCell.setInteger(options)
options{Object} 配置对象,包含:min{number} 最小值(默认 0)max{number} 最大值(默认 100)promptTitle{string} 提示标题(可选)promptText{string} 提示内容(可选)errorTitle{string} 错误标题(可选)errorText{string} 错误内容(可选)
- 返回 {ExcelCell}
添加整数范围验证。
// 仅设置范围
cell.setInteger({ min: 18, max: 60 });
// 带提示信息
cell.setInteger({
min: 18,
max: 60,
promptTitle: "年龄输入",
promptText: "请输入18-60之间的整数",
errorTitle: "错误",
errorText: "年龄必须在18-60之间"
});ExcelCell.setDecimal(options)
options{Object} 配置对象,包含:min{number} 最小值(默认 0.0)max{number} 最大值(默认 100.0)- 其他同
setInteger(promptTitle、promptText、errorTitle、errorText)
- 返回 {ExcelCell}
添加小数范围验证。
cell.setDecimal({ min: 0.0, max: 100.0 });ExcelCell.setDate(options)
options{Object} 配置对象,包含:min{string} 最小日期(格式:"yyyy-MM-dd",必需)max{string} 最大日期(格式:"yyyy-MM-dd",必需)- 其他同
setInteger(promptTitle、promptText、errorTitle、errorText)
- 返回 {ExcelCell}
添加日期范围验证。
cell.setDate({ min: "2020-01-01", max: "2025-12-31" });ExcelCell.setTime(options)
options{Object} 配置对象,包含:min{string} 最小时间(格式:"HH:mm",必需)max{string} 最大时间(格式:"HH:mm",必需)- 其他同
setInteger
- 返回 {ExcelCell}
添加时间范围验证。
cell.setTime({ min: "08:00", max: "18:00" });ExcelCell.setTextLength(options)
options{Object} 配置对象,包含:minLength{number} 最小长度(默认 0)maxLength{number} 最大长度(默认 255)- 其他同
setInteger
- 返回 {ExcelCell}
添加文本长度验证。
cell.setTextLength({ minLength: 5, maxLength: 10 });ExcelCell.removeValidation()
- 返回 {ExcelCell}
删除当前单元格的所有数据验证。
cell.removeValidation();ExcelCell.formula()
- 返回 {string | null}
获取公式字符串(如果单元格是公式类型)。
console.log("公式:", cell.formula());ExcelCell.setFormula(formula)
formula{string} 公式字符串(可带=前缀,将自动去除)- 返回 {ExcelCell}
设置公式。
cell.setFormula("SUM(A1:A10)");ExcelCell.hasFormula()
- 返回 {boolean}
判断是否为公式单元格。
if (cell.hasFormula()) {
console.log("是公式");
}ExcelCell.calculatedValue()
- 返回 {Object | null}
获取公式的计算结果(若单元格是公式类型)。
console.log("计算结果:", cell.calculatedValue());ExcelCell.text()
- 返回 {string}
获取格式化后的显示文本(根据单元格格式)。
console.log("显示文本:", cell.text());ExcelCell.text(evaluator)
evaluator{FormulaEvaluator} 公式求值器- 返回 {string}
使用指定求值器获取格式化文本(适用于公式单元格)。
var evaluator = excel.poiWorkbook().getCreationHelper().createFormulaEvaluator();
console.log("显示文本:", cell.text(evaluator));ExcelCell.setImage(image)
image{string | byte[] | Bitmap} 图片数据:- 字符串:图片文件路径(如
"/sdcard/logo.png") - 字节数组:图片的原始数据(
byte[]) - Bitmap:Android 的
Bitmap对象
- 字符串:图片文件路径(如
- 返回 {ExcelImage}
在当前单元格位置插入图片,返回图片构建器,可继续链式调用 .size()、.insert() 等方法。
// 从文件路径
cell.setImage("/sdcard/logo.png")
.size(200, 150)
.insert();
// 从字节数组
cell.setImage(imageData)
.size(100, 100)
.insert();
// 从 Bitmap
cell.setImage(bitmap)
.size(150, 150)
.insert();ExcelCellCollection
由 ExcelRow.cells() 等方法返回,代表单元格的集合。
ExcelCellCollection.size()
- 返回 {number}
获取单元格数量。
console.log("单元格数:", cells.size());ExcelCellCollection.empty()
- 返回 {boolean}
判断是否为空。
if (cells.empty()) {
console.log("没有单元格");
}ExcelCellCollection.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (cells.nonEmpty()) {
console.log("有单元格");
}ExcelCellCollection.get(index)
index{number} 索引- 返回 {ExcelCell | null}
获取指定索引的单元格。
var cell = cells.get(0);
console.log(cell.value());ExcelCellCollection.forEach(callback)
callback{Function} 回调函数(cell) => void- 返回 {ExcelCellCollection}
遍历所有单元格。
cells.forEach(function(cell) {
console.log(cell.value());
});
// 使用 size() + get() 遍历
for (var i = 0; i < cells.size(); i++) {
var cell = cells.get(i);
console.log(cell.value());
}ExcelCellCollection.filter(filter)
filter{Function} 过滤函数(cell) => boolean- 返回 {ExcelCellCollection}
过滤单元格。
var nonEmpty = cells.filter(function(cell) {
return !cell.empty();
});ExcelCellCollection.find(filter)
filter{Function} 过滤函数(cell) => boolean- 返回 {ExcelCell | null}
查找第一个匹配的单元格。
var found = cells.find(function(cell) {
return cell.value() === "目标值";
});ExcelCellCollection.style()
- 返回 {ExcelStyle}
为所有单元格设置统一样式。
cells.style()
.bold(true)
.background("#FFFF00");ExcelStyle
由 ExcelRange.style()、ExcelRow.style()、ExcelCell.style() 等方法返回,用于链式设置单元格样式。
完整测试示例
// ==================== ExcelStyle 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelStyleTest/
var TEST_DIR = "/sdcard/ExcelStyleTest/";
files.ensureDir(TEST_DIR);
// 打开(或创建)工作簿,并获取 Sheet
var excel = $excel.open({ path: TEST_DIR + "style_test.xlsx" });
var sheetName = "样式测试";
if (excel.hasSheet(sheetName)) {
excel.removeSheet(sheetName);
}
var sheet = excel.createSheet(sheetName);
// 辅助:写入一些基础数据
sheet.range("A1:E5").setValue([
["姓名", "年龄", "分数", "日期", "是否通过"],
["张三", 25, 90.5, "2024-01-15", true],
["李四", 30, 85.0, "2024-02-20", false],
["王五", 22, 76.5, "2024-03-10", true],
["赵六", 28, 92.0, "2024-04-05", false]
]);
console.log("\n========== ExcelStyle 样式测试 ==========");
// ==================== 1. 字体样式 ====================
console.log("\n--- 1. 字体样式 ---");
var rangeFont = sheet.range("A2:E2");
rangeFont.style()
.fontName("微软雅黑")
.fontSize(14)
.bold(true)
.italic(true)
.underline(1) // 单下划线
.strikeout(false)
.fontColor("#FF0000"); // 红色,字符串
console.log("字体样式已应用(A2:E2)");
// ==================== 2. 对齐与换行 ====================
console.log("\n--- 2. 对齐与换行 ---");
var rangeAlign = sheet.range("A3:E3");
rangeAlign.style()
.align("center")
.vertical("middle")
.wrap(true);
console.log("对齐样式已应用(A3:E3)");
// ==================== 3. 背景色(字符串和整数) ====================
console.log("\n--- 3. 背景色 ---");
var rangeBg = sheet.range("A4:E4");
rangeBg.style()
.background("#FFFF00"); // 字符串
console.log("背景色(字符串)已应用(A4:E4)");
var rangeBgInt = sheet.range("A5:E5");
rangeBgInt.style()
.background(0xFF00FF); // 整数(品红)
console.log("背景色(整数)已应用(A5:E5)");
// ==================== 4. 边框 ====================
console.log("\n--- 4. 边框 ---");
var rangeBorder = sheet.range("A7:E9");
rangeBorder.setValue([
["边框", "测试", "区域", "A", "B"],
["上", "下", "左", "右", "外"],
["内", "全", "顶", "底", "边"]
]);
// 外边框(字符串颜色)
rangeBorder.style()
.border("thin", "#0000FF") // 蓝色外边框
.insideBorder("dotted", "#CCCCCC") // 灰色虚线内边框
.allBorder("double", "#FF0000"); // 红色双线所有边框(会覆盖外边框和内边框,为了演示只保留最后一个)
console.log("边框已应用(A7:E9)");
// 单独测试单个边框(使用整数颜色)
var rangeSingle = sheet.range("A11:E11");
rangeSingle.setValue(["上边框", "下边框", "左边框", "右边框", "组合"]);
rangeSingle.style()
.borderTop("medium", 0xFF0000)
.borderBottom("thick", 0x00FF00)
.borderLeft("dashed", 0x0000FF)
.borderRight("double", 0xFFFF00);
console.log("单边边框已应用(A11:E11)");
// ==================== 5. 数字格式 ====================
console.log("\n--- 5. 数字格式 ---");
var rangeFormat = sheet.range("C2:C5");
rangeFormat.style()
.format("#,##0.00"); // 千分位小数
console.log("数字格式已应用(C2:C5)");
// 日期格式
var rangeDate = sheet.range("D2:D5");
rangeDate.style()
.format("yyyy-MM-dd");
console.log("日期格式已应用(D2:D5)");
// 百分比
var rangePercent = sheet.range("B12");
rangePercent.setValue(0.85);
rangePercent.style()
.format("0.00%");
console.log("百分比格式已应用(B12)");
// ==================== 6. 锁定与隐藏 ====================
console.log("\n--- 6. 锁定与隐藏 ---");
var rangeLock = sheet.range("A13:C13");
rangeLock.setValue(["锁定", "隐藏", "测试"]);
rangeLock.style()
.lock(true)
.hidden(true);
console.log("锁定与隐藏已应用(A13:C13)");
// ==================== 7. 样式复制 ====================
console.log("\n--- 7. 样式复制 ---");
var sourceStyle = sheet.range("A2:E2").style(); // 获取现有样式(但无法直接复制,需创建新样式)
// 创建一个新样式并复制
var targetRange = sheet.range("A15:E15");
targetRange.setValue(["复制", "样式", "测试", "A", "B"]);
var newStyle = targetRange.style()
.bold(true)
.fontColor("#00FF00");
// 复制源样式(从另一个样式对象复制)
// 由于无法直接获取已有样式对象,我们创建一个临时样式并复制
var tempStyle = sheet.range("A2:E2").style();
// 注意:copy 方法需要传入 ExcelStyle 对象,但 range.style() 返回的是新实例,不能直接获取现有样式。
// 为了测试 copy,我们创建一个样式并复制属性
var copyTarget = sheet.range("A17:E17");
copyTarget.setValue(["复制", "样式", "测试", "C", "D"]);
var styleToCopy = copyTarget.style()
.bold(true)
.fontColor("#FF0000")
.background("#FFFF00");
// 现在复制到另一个范围
var pasteRange = sheet.range("A19:E19");
pasteRange.setValue(["粘贴", "样式", "测试", "E", "F"]);
pasteRange.style().copy(styleToCopy);
console.log("样式复制完成(A19:E19 复制了 A17:E17 的样式)");
// ==================== 8. 颜色整数测试(多种格式) ====================
console.log("\n--- 8. 颜色整数测试 ---");
var rangeIntColor = sheet.range("A21:E21");
rangeIntColor.setValue(["整数颜色", "RGB", "ARGB", "混合", "测试"]);
rangeIntColor.style()
.fontColor(0xFF8800) // 橙色
.background(0x88FF00) // 亮绿色
.border("thin", 0x0000FF); // 蓝色边框
console.log("整数颜色已应用(A21:E21)");
// ==================== 9. 综合样式(链式调用) ====================
console.log("\n--- 9. 综合样式(链式调用) ---");
var rangeComplex = sheet.range("A23:E25");
rangeComplex.setValue([
["综合", "样式", "测试", "区域", "A"],
["行1", "行2", "行3", "行4", "行5"],
["结束", "测试", "完成", "OK", "!"]
]);
rangeComplex.style()
.fontName("Arial")
.fontSize(16)
.bold(true)
.italic(false)
.underline(2) // 双下划线
.strikeout(true)
.fontColor("#FFFFFF")
.align("center")
.vertical("middle")
.wrap(false)
.background("#4472C4")
.border("medium", "#FF0000")
.insideBorder("thin", "#CCCCCC")
.allBorder("thin", "#0000FF") // 覆盖之前的边框,为了演示可注释掉
.format("0.00")
.lock(true)
.hidden(false);
console.log("综合样式已应用(A23:E25)");
// ==================== 10. 边框带颜色(字符串和整数混合) ====================
console.log("\n--- 10. 边框颜色混合 ---");
var rangeMix = sheet.range("A27:E27");
rangeMix.setValue(["混合", "边框", "颜色", "测试", "完成"]);
rangeMix.style()
.borderTop("thin", "#FF00FF")
.borderBottom("medium", 0x00FFFF)
.borderLeft("thick", "#00FF00")
.borderRight("double", 0xFF0000);
console.log("混合边框颜色已应用(A27:E27)");
// ==================== 保存文件 ====================
excel.save({ path: TEST_DIR + "style_final.xlsx" });
console.log("\n✅ 所有样式测试完成!");
console.log("📁 生成的文件位于:", TEST_DIR);
console.log(" 文件: style_final.xlsx");
excel.close();
console.log("测试结束。");ExcelStyle.fontName(name)
name{string} 字体名称- 返回 {ExcelStyle}
设置字体名称。
style.fontName("微软雅黑");ExcelStyle.fontSize(size)
size{number} 字体大小(单位:点)- 返回 {ExcelStyle}
设置字体大小。
style.fontSize(12);ExcelStyle.bold(bold)
bold{boolean} 是否加粗- 返回 {ExcelStyle}
设置加粗。
style.bold(true);ExcelStyle.italic(italic)
italic{boolean} 是否斜体- 返回 {ExcelStyle}
设置斜体。
style.italic(true);ExcelStyle.underline(type)
type{number} 下划线类型:0:无下划线1:单下划线2:双下划线- (其他值:
33为单会计下划线,34为双会计下划线,较少使用)
- 返回 {ExcelStyle}
设置下划线。
style.underline(1); // 单下划线
style.underline(2); // 双下划线
style.underline(0); // 取消下划线ExcelStyle.strikeout(strikeout)
strikeout{boolean} 是否删除线- 返回 {ExcelStyle}
设置删除线。
style.strikeout(true);ExcelStyle.fontColor(color)
color{string | number} 十六进制颜色字符串(如"#FF0000")或 32 位整数颜色值(如0xFF0000或0xFFFF0000)- 返回 {ExcelStyle}
设置字体颜色。整数形式支持 ARGB(Alpha 通道会被忽略)。
style.fontColor("#FF0000");
style.fontColor(0xFF0000);
style.fontColor(0xFFFF0000); // Alpha 被忽略ExcelStyle.align(align)
align{string} 水平对齐方式:"left"、"center"、"right"- 返回 {ExcelStyle}
设置水平对齐。
style.align("center");ExcelStyle.vertical(align)
align{string} 垂直对齐方式:"top"、"middle"、"bottom"- 返回 {ExcelStyle}
设置垂直对齐。
style.vertical("middle");ExcelStyle.wrap(wrap)
wrap{boolean} 是否自动换行- 返回 {ExcelStyle}
设置自动换行。
style.wrap(true);ExcelStyle.background(color)
color{string | number} 十六进制颜色字符串或 32 位整数颜色值- 返回 {ExcelStyle}
设置背景色。整数形式支持 ARGB(Alpha 通道会被忽略)。
style.background("#FFFF00");
style.background(0xFFFF00);ExcelStyle.border(styleStr[, color])
styleStr{string} 边框样式:"thin"、"medium"、"thick"、"double"、"dotted"、"dashed"、"hair"、"none"color{string | number} 可选,边框颜色(十六进制字符串或 32 位整数)- 返回 {ExcelStyle}
设置外边框。
style.border("thin");
style.border("thin", "#000000");
style.border("thin", 0x000000);ExcelStyle.insideBorder(styleStr[, color])
styleStr{string} 边框样式color{string | number} 可选,边框颜色(十六进制字符串或 32 位整数)- 返回 {ExcelStyle}
设置内部边框(仅对多行多列区域有效)。
style.insideBorder("thin");
style.insideBorder("thin", "#CCCCCC");
style.insideBorder("thin", 0xCCCCCC);ExcelStyle.allBorder(styleStr[, color])
styleStr{string} 边框样式color{string | number} 可选,边框颜色(十六进制字符串或 32 位整数)- 返回 {ExcelStyle}
设置所有单元格的边框(每个单元格都有边框)。
style.allBorder("thin");
style.allBorder("thin", "#000000");
style.allBorder("thin", 0x000000);ExcelStyle.borderTop(styleStr[, color])
styleStr{string} 边框样式color{string | number} 可选,边框颜色(十六进制字符串或 32 位整数)- 返回 {ExcelStyle}
设置上边框。
style.borderTop("thin");
style.borderTop("thin", "#FF0000");
style.borderTop("thin", 0xFF0000);ExcelStyle.borderBottom(styleStr[, color])
styleStr{string} 边框样式color{string | number} 可选,边框颜色(十六进制字符串或 32 位整数)- 返回 {ExcelStyle}
设置下边框。
style.borderBottom("thin");
style.borderBottom("thin", "#FF0000");
style.borderBottom("thin", 0xFF0000);ExcelStyle.borderLeft(styleStr[, color])
styleStr{string} 边框样式color{string | number} 可选,边框颜色(十六进制字符串或 32 位整数)- 返回 {ExcelStyle}
设置左边框。
style.borderLeft("thin");
style.borderLeft("thin", "#FF0000");
style.borderLeft("thin", 0xFF0000);ExcelStyle.borderRight(styleStr[, color])
styleStr{string} 边框样式color{string | number} 可选,边框颜色(十六进制字符串或 32 位整数)- 返回 {ExcelStyle}
设置右边框。
style.borderRight("thin");
style.borderRight("thin", "#FF0000");
style.borderRight("thin", 0xFF0000);ExcelStyle.format(format)
format{string} Excel 格式字符串- 返回 {ExcelStyle}
设置单元格数据格式。
style.format("#,##0.00");
style.format("0.00%");
style.format("yyyy-MM-dd");
style.format("@");
style.format("¥#,##0.00");
style.format("HH:mm:ss");ExcelStyle.lock(locked)
locked{boolean} 是否锁定- 返回 {ExcelStyle}
设置单元格锁定状态(需配合工作表保护生效)。
style.lock(true);ExcelStyle.hidden(hidden)
hidden{boolean} 是否隐藏公式- 返回 {ExcelStyle}
设置公式隐藏状态(需配合工作表保护生效)。
style.hidden(true);ExcelStyle.copy(source)
source{ExcelStyle} 源样式对象- 返回 {ExcelStyle}
从另一个样式复制所有属性到当前样式。
var sourceStyle = range.style().bold(true).background("#FFFF00");
style.copy(sourceStyle);ExcelImage
由 ExcelCell.setImage(image) 等方法返回,用于构建和插入图片。
完整测试示例
// ==================== ExcelImage 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelImageTest/
// 请将以下图片路径替换为实际存在的图片路径
var TEST_DIR = "/sdcard/ExcelImageTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({ path: TEST_DIR + "image_test.xlsx" });
var sheet = excel.sheet("图片测试");
if (!sheet) sheet = excel.createSheet("图片测试");
// 测试图片路径(请根据实际情况修改)
var img1 = "/sdcard/test.png";
var img2 = "/sdcard/logo.png";
var img3 = "/sdcard/photo.jpg";
var img4 = "/sdcard/icon.png";
var img5 = "/sdcard/photo2.jpg";
console.log("\n========== ExcelImage API 测试 ==========");
// 辅助函数:在指定单元格写入说明文字
function writeLabel(address, text) {
var cell = sheet.cell(address);
cell.setValue(text);
}
// ==================== 1. 使用 sheet.image() + at(string) + size ====================
if (files.isFile(img1)) {
writeLabel("A1", "图片1 (200x150)");
sheet.image(img1)
.at("A1")
.size(200, 150)
.insert();
console.log("✅ 图片1 插入到 A1,尺寸 200x150");
} else {
console.log("⚠️ 图片1 不存在: " + img1);
}
// ==================== 2. 使用 sheet.image() + at(row, col) + width/height ====================
if (files.isFile(img2)) {
writeLabel("D4", "图片2 (宽300, 高200)");
sheet.image(img2)
.at(3, 3) // 第4行第4列 (D4)
.width(300)
.height(200)
.insert();
console.log("✅ 图片2 插入到 D4,宽度300,高度200");
} else {
console.log("⚠️ 图片2 不存在: " + img2);
}
// ==================== 3. 使用 cell.setImage() ====================
if (files.isFile(img3)) {
var cell = sheet.cell("G1");
cell.setValue("图片3 (100x100)");
cell.setImage(img3)
.size(100, 100)
.insert();
console.log("✅ 图片3 插入到 G1,尺寸 100x100");
} else {
console.log("⚠️ 图片3 不存在: " + img3);
}
// ==================== 4. 从字节数组插入 ====================
if (files.isFile(img4)) {
var bytes = files.readBytes(img4);
writeLabel("A5", "字节数组图片 (150x150)");
sheet.image(bytes)
.at("A5")
.size(150, 150)
.insert();
console.log("✅ 字节数组图片插入到 A5");
} else {
console.log("⚠️ 字节数组图片不存在: " + img4);
}
// ==================== 5. 仅设置宽度(高度使用默认或保持比例) ====================
if (files.isFile(img5)) {
writeLabel("C10", "仅宽度300");
sheet.image(img5)
.at("C10")
.width(300)
.insert();
console.log("✅ 图片5 仅设置宽度300,插入到 C10");
} else {
console.log("⚠️ 图片5 不存在: " + img5);
}
// ==================== 6. 测试无效图片(异常处理) ====================
console.log("\n--- 异常测试 ---");
try {
sheet.image("/sdcard/not_exist.png")
.at("A10")
.insert();
} catch (e) {
console.log("✅ 捕获到预期异常(文件不存在):", e.message);
}
// 保存最终文件
excel.save({ path: TEST_DIR + "image_final.xlsx" });
console.log("\n✅ 所有测试完成!");
console.log("📁 生成的文件: image_final.xlsx");
excel.close();ExcelImage.at(address)
address{string} 单元格地址,如"A1"- 返回 {ExcelImage}
设置图片位置(单元格地址)。
sheet.image("/sdcard/logo.png")
.at("A1");ExcelImage.at(row, col)
row{number} 行索引col{number} 列索引- 返回 {ExcelImage}
设置图片位置(行列索引)。
sheet.image("/sdcard/logo.png")
.at(0, 0);ExcelImage.size(width, height)
width{number} 宽度(像素)height{number} 高度(像素)- 返回 {ExcelImage}
设置图片尺寸。
sheet.image("/sdcard/logo.png")
.size(200, 150);ExcelImage.width(width)
width{number} 宽度(像素)- 返回 {ExcelImage}
设置图片宽度。
sheet.image("/sdcard/logo.png")
.width(300);ExcelImage.height(height)
height{number} 高度(像素)- 返回 {ExcelImage}
设置图片高度。
sheet.image("/sdcard/logo.png")
.height(200);ExcelImage.insert()
- 异常 {IOException}
插入图片到工作表。
sheet.image("/sdcard/logo.png")
.at("A1")
.size(200, 150)
.insert();ExcelValidation
由 ExcelValidationCollection.get(index) 等方法返回,代表一个数据验证。
完整测试示例
// ==================== ExcelValidation 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelValidationTest/
var TEST_DIR = "/sdcard/ExcelValidationTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({ path: TEST_DIR + "validation_detail_test.xlsx" });
var sheet = excel.sheet("验证详情");
if (!sheet) sheet = excel.createSheet("验证详情");
// 1. 创建表头
sheet.createHeader(["字段", "规则", "示例值"]);
// 2. 添加多种数据验证(每个验证设置不同的提示和错误信息,以便测试)
// 下拉列表 (A2) - 显式列表
sheet.cell(1, 0).setValue("性别");
sheet.cell(1, 1).setValue("下拉列表");
sheet.cell(1, 2).setDropdown(["男", "女", "其他"]);
// 整数验证 (A3) - 带提示和错误信息
sheet.cell(2, 0).setValue("年龄");
sheet.cell(2, 1).setValue("整数 18-60");
sheet.cell(2, 2).setInteger({
min: 18,
max: 60,
promptTitle: "年龄输入",
promptText: "请输入18-60之间的整数",
errorTitle: "年龄错误",
errorText: "年龄必须在18-60之间"
});
// 小数验证 (A4) - 只设置范围,无提示
sheet.cell(3, 0).setValue("分数");
sheet.cell(3, 1).setValue("小数 0-100");
sheet.cell(3, 2).setDecimal({ min: 0.0, max: 100.0 });
// 日期验证 (A5) - 带提示
sheet.cell(4, 0).setValue("日期");
sheet.cell(4, 1).setValue("日期范围");
sheet.cell(4, 2).setDate({
min: "2020-01-01",
max: "2025-12-31",
promptTitle: "日期输入",
promptText: "请输入2020-2025之间的日期"
});
// 文本长度验证 (A6) - 带错误信息
sheet.cell(5, 0).setValue("文本");
sheet.cell(5, 1).setValue("长度 5-10");
sheet.cell(5, 2).setTextLength({
minLength: 5,
maxLength: 10,
errorTitle: "文本长度错误",
errorText: "长度必须在5-10之间"
});
// 3. 获取所有验证
var validations = sheet.validations();
console.log("\n========== ExcelValidation 详细 API 测试 ==========");
console.log("验证总数:", validations.size());
// 4. 遍历每个验证,测试所有 API
validations.forEach(function(validation) {
console.log("\n--- 验证 ---");
// type()
console.log("type():", validation.type());
// operator() (对于 LIST 类型可能为 null)
console.log("operator():", validation.operator());
// formula1() / formula2() (通常为 null)
console.log("formula1():", validation.formula1());
console.log("formula2():", validation.formula2());
// listValues() (仅 LIST 类型有值)
var listVals = validation.listValues();
console.log("listValues():", listVals ? JSON.stringify(listVals) : "null");
// allowBlank()
console.log("allowBlank():", validation.allowBlank());
// showPrompt()
console.log("showPrompt():", validation.showPrompt());
// showError()
console.log("showError():", validation.showError());
// promptTitle() / promptText()
console.log("promptTitle():", validation.promptTitle());
console.log("promptText():", validation.promptText());
// errorTitle() / errorText()
console.log("errorTitle():", validation.errorTitle());
console.log("errorText():", validation.errorText());
// suppressDropDownArrow()
console.log("suppressDropDownArrow():", validation.suppressDropDownArrow());
// regions()
var regions = validation.regions();
console.log("regions():", JSON.stringify(regions));
// explicitList()
console.log("explicitList():", validation.explicitList());
// poiDataValidation() (仅验证对象存在)
var poiDV = validation.poiDataValidation();
console.log("poiDataValidation():", poiDV ? "获取成功" : "获取失败");
});
// 5. 额外测试:单独获取一个验证并测试(例如第一个)
console.log("\n--- 单独测试第一个验证(下拉列表) ---");
var firstValidation = validations.get(0);
if (firstValidation) {
console.log("type():", firstValidation.type());
console.log("listValues():", JSON.stringify(firstValidation.listValues()));
console.log("regions():", JSON.stringify(firstValidation.regions()));
console.log("explicitList():", firstValidation.explicitList());
}
// 6. 空集合测试(新建空 Sheet)
var emptySheet = excel.createSheet("空Sheet");
var emptyValidations = emptySheet.validations();
console.log("\n--- 空集合测试 ---");
console.log("空集合 size():", emptyValidations.size());
// 保存最终文件
excel.save({ path: TEST_DIR + "validation_detail_final.xlsx" });
console.log("\n✅ 测试完成!");
console.log("📁 生成的文件:", TEST_DIR + "validation_detail_final.xlsx");
excel.close();ExcelValidation.type()
- 返回 {string}
获取验证类型("LIST"、"INTEGER"、"DECIMAL"、"DATE"、"TIME"、"TEXT_LENGTH"、"CUSTOM"、"ANY"、"UNKNOWN")。
console.log("类型:", validation.type());ExcelValidation.operator()
- 返回 {string} |
null
获取运算符("BETWEEN"、"NOT_BETWEEN"、"EQUAL"、"NOT_EQUAL"、"GREATER_THAN"、"LESS_THAN"、"GREATER_OR_EQUAL"、"LESS_OR_EQUAL")。
console.log("运算符:", validation.operator());ExcelValidation.formula1()
- 返回 {string} |
null
获取公式1。
console.log("公式1:", validation.formula1());ExcelValidation.formula2()
- 返回 {string} |
null
获取公式2。
console.log("公式2:", validation.formula2());ExcelValidation.listValues()
- 返回 {Array<string> |
null}
获取下拉列表内容(仅显式列表)。
var values = validation.listValues();
if (values) {
console.log("下拉选项:", values);
}ExcelValidation.allowBlank()
- 返回 {boolean}
是否允许空值。
if (validation.allowBlank()) {
console.log("允许空值");
}ExcelValidation.showPrompt()
- 返回 {boolean}
是否显示输入提示。
if (validation.showPrompt()) {
console.log("显示输入提示");
}ExcelValidation.showError()
- 返回 {boolean}
是否显示错误提示。
if (validation.showError()) {
console.log("显示错误提示");
}ExcelValidation.promptTitle()
- 返回 {string} |
null
获取提示标题。
console.log("提示标题:", validation.promptTitle());ExcelValidation.promptText()
- 返回 {string} |
null
获取提示内容。
console.log("提示内容:", validation.promptText());ExcelValidation.errorTitle()
- 返回 {string} |
null
获取错误标题。
console.log("错误标题:", validation.errorTitle());ExcelValidation.errorText()
- 返回 {string} |
null
获取错误内容。
console.log("错误内容:", validation.errorText());ExcelValidation.suppressDropDownArrow()
- 返回 {boolean}
是否隐藏下拉箭头。
if (validation.suppressDropDownArrow()) {
console.log("隐藏下拉箭头");
}ExcelValidation.regions()
- 返回 {Array<string>}
获取验证区域列表。
var regions = validation.regions();
for (var i = 0; i < regions.length; i++) {
console.log("区域:", regions[i]);
}ExcelValidation.explicitList()
- 返回 {boolean}
是否为显式下拉列表。
if (validation.explicitList()) {
console.log("是显式下拉列表");
}ExcelValidation.poiDataValidation()
获取底层 POI DataValidation 对象,用于执行 POI 原生的数据验证操作。
var poiValidation = validation.poiDataValidation();ExcelValidationCollection
由 ExcelSheet.validations() 返回,代表数据验证的集合。
完整测试示例
// ==================== ExcelValidationCollection 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/ExcelValidationCollectionTest/
var TEST_DIR = "/sdcard/ExcelValidationCollectionTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({ path: TEST_DIR + "validation_test.xlsx" });
var sheet = excel.sheet("验证测试");
if (!sheet) sheet = excel.createSheet("验证测试");
// 1. 创建表头
sheet.createHeader(["字段", "规则", "示例值"]);
// 2. 添加多种数据验证到不同单元格
// 下拉列表 (A2)
sheet.cell(1, 0).setValue("性别");
sheet.cell(1, 1).setValue("下拉列表");
sheet.cell(1, 2).setDropdown(["男", "女", "其他"]);
// 整数验证 (A3)
sheet.cell(2, 0).setValue("年龄");
sheet.cell(2, 1).setValue("整数 18-60");
sheet.cell(2, 2).setInteger({ min: 18, max: 60 });
// 小数验证 (A4)
sheet.cell(3, 0).setValue("分数");
sheet.cell(3, 1).setValue("小数 0-100");
sheet.cell(3, 2).setDecimal({ min: 0.0, max: 100.0 });
// 日期验证 (A5)
sheet.cell(4, 0).setValue("日期");
sheet.cell(4, 1).setValue("日期范围");
sheet.cell(4, 2).setDate({ min: "2020-01-01", max: "2025-12-31" });
// 文本长度验证 (A6)
sheet.cell(5, 0).setValue("文本");
sheet.cell(5, 1).setValue("长度 5-10");
sheet.cell(5, 2).setTextLength({ minLength: 5, maxLength: 10 });
// 3. 获取验证集合
var validations = sheet.validations();
console.log("\n========== ExcelValidationCollection 测试 ==========");
// 4. 测试 size()
console.log("size():", validations.size()); // 预期 5
// 5. 测试 empty() 和 nonEmpty()
console.log("empty():", validations.empty()); // false
console.log("nonEmpty():", validations.nonEmpty()); // true
// 6. 测试 get(index)
console.log("\n--- get(index) ---");
for (var i = 0; i < validations.size(); i++) {
var v = validations.get(i);
console.log("get(" + i + ").type():", v.type());
// regions() 返回 NativeArray,需要转为字符串
console.log(" regions:", JSON.stringify(v.regions()));
if (v.type() === "LIST") {
// listValues() 返回 NativeArray
console.log(" listValues:", JSON.stringify(v.listValues()));
}
}
// 7. 测试 forEach(callback)
console.log("\n--- forEach 遍历 ---");
validations.forEach(function(validation) {
console.log("类型:", validation.type(), "区域:", JSON.stringify(validation.regions()));
});
// 8. 测试 filter(filter)
console.log("\n--- filter ---");
var listValidations = validations.filter(function(validation) {
return validation.type() === "LIST";
});
console.log("过滤后 (LIST) size:", listValidations.size());
// 使用 size/get 遍历过滤结果
console.log("过滤后验证(使用 size/get 遍历):");
for (var i = 0; i < listValidations.size(); i++) {
var v = listValidations.get(i);
console.log(" 验证 " + i + ": 类型=" + v.type() + ", 区域=" + JSON.stringify(v.regions()));
if (v.type() === "LIST") {
console.log(" listValues:", JSON.stringify(v.listValues()));
}
}
// 9. 测试 find(filter)
console.log("\n--- find ---");
var found = validations.find(function(validation) {
return validation.type() === "INTEGER";
});
console.log("找到的第一个 INTEGER 验证:", found ? "类型=" + found.type() + ", 区域=" + JSON.stringify(found.regions()) : "未找到");
// 10. 额外:空集合测试(新建一个空 Sheet)
var emptySheet = excel.createSheet("空Sheet");
var emptyValidations = emptySheet.validations();
console.log("\n--- 空集合测试 ---");
console.log("空集合 size():", emptyValidations.size());
console.log("空集合 empty():", emptyValidations.empty());
console.log("空集合 nonEmpty():", emptyValidations.nonEmpty());
console.log("空集合 get(0):", emptyValidations.get(0)); // 应返回 null
// 保存最终文件
excel.save({ path: TEST_DIR + "validation_final.xlsx" });
console.log("\n✅ 测试完成!");
console.log("📁 生成的文件:", TEST_DIR + "validation_final.xlsx");
excel.close();ExcelValidationCollection.size()
- 返回 {number}
获取数据验证数量。
console.log("数据验证数:", validations.size());ExcelValidationCollection.empty()
- 返回 {boolean}
判断是否为空。
if (validations.empty()) {
console.log("没有数据验证");
}ExcelValidationCollection.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (validations.nonEmpty()) {
console.log("有数据验证");
}ExcelValidationCollection.get(index)
index{number} 索引- 返回 {ExcelValidation} |
null
获取指定索引的数据验证。
var validation = validations.get(0);ExcelValidationCollection.forEach(callback)
callback{Function} 回调函数(validation) => void- 返回 {ExcelValidationCollection}
遍历所有数据验证。
validations.forEach(function(validation) {
console.log("类型:", validation.type());
console.log("区域:", validation.regions());
});ExcelValidationCollection.filter(filter)
filter{Function} 过滤函数(validation) => boolean- 返回 {ExcelValidationCollection}
过滤数据验证。
var listValidations = validations.filter(function(validation) {
return validation.type() === "LIST";
});ExcelValidationCollection.find(filter)
filter{Function} 过滤函数(validation) => boolean- 返回 {ExcelValidation} |
null
查找第一个匹配的数据验证。
var found = validations.find(function(validation) {
return validation.type() === "INTEGER";
});ExcelComment
由 ExcelCell.comment() 等方法返回,代表一个批注。
ExcelComment.text()
- 返回 {string}
获取批注文本。
console.log("批注:", comment.text());ExcelComment.author()
- 返回 {string}
获取批注作者。
console.log("作者:", comment.author());ExcelComment.poiComment()
获取底层 POI Comment 对象,用于执行 POI 原生的批注操作(如设置作者、修改文本内容等)。
var poiComment = comment.poiComment();ExcelCommentCollection
由 ExcelRange.comments() 等方法返回,代表批注的集合。
ExcelCommentCollection.size()
- 返回 {number}
获取批注数量。
console.log("批注数:", comments.size());ExcelCommentCollection.empty()
- 返回 {boolean}
判断是否为空。
if (comments.empty()) {
console.log("没有批注");
}ExcelCommentCollection.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (comments.nonEmpty()) {
console.log("有批注");
}ExcelCommentCollection.get(index)
index{number} 索引- 返回 {ExcelComment | null}
获取指定索引的批注。
var comment = comments.get(0);
console.log(comment.text());ExcelCommentCollection.forEach(callback)
callback{Function} 回调函数(comment) => void- 返回 {ExcelCommentCollection}
遍历所有批注。
comments.forEach(function(comment) {
console.log("批注:", comment.text(), "作者:", comment.author());
});
// 使用 size() + get() 遍历
for (var i = 0; i < comments.size(); i++) {
var c = comments.get(i);
console.log(c.text());
}ExcelCommentCollection.filter(filter)
filter{Function} 过滤函数(comment) => boolean- 返回 {ExcelCommentCollection}
过滤批注。
var authorComments = comments.filter(function(comment) {
return comment.author() === "张三";
});ExcelCommentCollection.find(filter)
filter{Function} 过滤函数(comment) => boolean- 返回 {ExcelComment | null}
查找第一个匹配的批注。
var found = comments.find(function(comment) {
return comment.text().indexOf("重要") !== -1;
});ExcelHyperlink
由 ExcelCell.hyperlink() 等方法返回,代表一个超链接。
ExcelHyperlink.address()
- 返回 {string}
获取超链接地址。
console.log("地址:", link.address());ExcelHyperlink.label()
- 返回 {string} |
null
获取超链接标签(显示文本)。
console.log("标签:", link.label());ExcelHyperlink.type()
- 返回 {string}
获取超链接类型("URL"、"EMAIL"、"FILE" 等)。
console.log("类型:", link.type());ExcelHyperlink.poiHyperlink()
获取底层 POI Hyperlink 对象,用于执行 POI 原生的超链接操作(如修改地址、类型等)。
var poiLink = link.poiHyperlink();ExcelHyperlinkCollection
由 ExcelRange.hyperlinks() 等方法返回,代表超链接的集合。
ExcelHyperlinkCollection.size()
- 返回 {number}
获取超链接数量。
console.log("超链接数:", links.size());ExcelHyperlinkCollection.empty()
- 返回 {boolean}
判断是否为空。
if (links.empty()) {
console.log("没有超链接");
}ExcelHyperlinkCollection.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (links.nonEmpty()) {
console.log("有超链接");
}ExcelHyperlinkCollection.get(index)
index{number} 索引- 返回 {ExcelHyperlink | null}
获取指定索引的超链接。
var link = links.get(0);
console.log(link.address());ExcelHyperlinkCollection.forEach(callback)
callback{Function} 回调函数(link) => void- 返回 {ExcelHyperlinkCollection}
遍历所有超链接。
links.forEach(function(link) {
console.log("地址:", link.address(), "类型:", link.type());
});
// 使用 size() + get() 遍历
for (var i = 0; i < links.size(); i++) {
var link = links.get(i);
console.log(link.address());
}ExcelHyperlinkCollection.filter(filter)
filter{Function} 过滤函数(link) => boolean- 返回 {ExcelHyperlinkCollection}
过滤超链接。
var urlLinks = links.filter(function(link) {
return link.type() === "URL";
});ExcelHyperlinkCollection.find(filter)
filter{Function} 过滤函数(link) => boolean- 返回 {ExcelHyperlink | null}
查找第一个匹配的超链接。
var found = links.find(function(link) {
return link.address() === "https://example.com";
});CellRangeAddressCollection
由 ExcelSheet.mergedRegions() 返回,代表合并区域地址的集合。
完整测试示例
// ==================== CellRangeAddressCollection 完整 API 测试 ====================
// 运行前请确保存储权限已开启,测试文件将生成在 /sdcard/CellRangeAddressCollectionTest/
var TEST_DIR = "/sdcard/CellRangeAddressCollectionTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({ path: TEST_DIR + "merged_regions_test.xlsx" });
var sheet = excel.sheet("测试");
if (!sheet) sheet = excel.createSheet("测试");
// 1. 创建一些合并区域(不同位置)
sheet.range("A1:B2").merge(); // 区域1
sheet.range("D4:E6").merge(); // 区域2
sheet.range("G7:H8").merge(); // 区域3
sheet.range("J10:K11").merge(); // 区域4
// 2. 获取合并区域集合
var merged = sheet.mergedRegions();
console.log("\n========== CellRangeAddressCollection 测试 ==========");
// 3. 测试 size()
console.log("size():", merged.size()); // 预期 4
// 4. 测试 empty() 和 nonEmpty()
console.log("empty():", merged.empty()); // false
console.log("nonEmpty():", merged.nonEmpty()); // true
// 5. 测试 get(index)
console.log("\n--- get(index) ---");
for (var i = 0; i < merged.size(); i++) {
var range = merged.get(i);
console.log("get(" + i + ").formatAsString():", range.formatAsString());
}
// 6. 测试 forEach(callback)(不带索引)
console.log("\n--- forEach 遍历 ---");
merged.forEach(function(range) {
console.log("合并区域:", range.formatAsString());
});
// 7. 测试 filter(filter)
console.log("\n--- filter ---");
// 过滤出起始行 > 3 的区域(即区域2、区域3、区域4)
var filtered = merged.filter(function(range) {
return range.getFirstRow() > 3;
});
console.log("过滤后 size:", filtered.size());
// 使用 size() + get() 遍历过滤结果
console.log("过滤后区域(使用 size/get 遍历):");
for (var i = 0; i < filtered.size(); i++) {
var r = filtered.get(i);
console.log(" 区域 " + i + ":", r.formatAsString());
}
// 8. 测试 find(filter)
console.log("\n--- find ---");
var found = merged.find(function(range) {
// 查找第一个起始列 > 5 的区域(即区域3、区域4)
return range.getFirstColumn() > 5;
});
console.log("找到的区域(第一个起始列>5):", found ? found.formatAsString() : "未找到");
// 9. 额外:测试空集合的情况(新建一个空 Sheet)
var emptySheet = excel.createSheet("空Sheet");
var emptyMerged = emptySheet.mergedRegions();
console.log("\n--- 空集合测试 ---");
console.log("空集合 size():", emptyMerged.size());
console.log("空集合 empty():", emptyMerged.empty());
console.log("空集合 nonEmpty():", emptyMerged.nonEmpty());
// 保存最终文件
excel.save({ path: TEST_DIR + "merged_final.xlsx" });
console.log("\n✅ 测试完成!");
console.log("📁 生成的文件:", TEST_DIR + "merged_final.xlsx");
excel.close();CellRangeAddressCollection.size()
- 返回 {number}
获取合并区域数量。
console.log("合并区域数:", merged.size());CellRangeAddressCollection.empty()
- 返回 {boolean}
判断是否为空。
if (merged.empty()) {
console.log("没有合并区域");
}CellRangeAddressCollection.nonEmpty()
- 返回 {boolean}
判断是否非空。
if (merged.nonEmpty()) {
console.log("有合并区域");
}CellRangeAddressCollection.get(index)
index{number} 索引- 返回 {org.apache.poi.ss.util.CellRangeAddress} |
null
获取指定索引的合并区域。
var range = merged.get(0);
console.log(range.formatAsString());CellRangeAddressCollection.forEach(consumer)
consumer{Function} 回调函数(range) => void- 返回 {CellRangeAddressCollection}
遍历所有合并区域。
merged.forEach(function(range) {
console.log(range.formatAsString());
});CellRangeAddressCollection.filter(filter)
filter{Function} 过滤函数(range) => boolean- 返回 {CellRangeAddressCollection}
过滤合并区域。
var filtered = merged.filter(function(range) {
return range.getFirstRow() > 5;
});CellRangeAddressCollection.find(filter)
filter{Function} 过滤函数(range) => boolean- 返回 {CellRangeAddress} |
null
查找第一个匹配的合并区域。
var found = merged.find(function(range) {
return range.formatAsString() === "A1:B2";
});实战示例
所有示例文件均保存在
/sdcard/ExcelTest/目录,运行前会自动创建该目录。
创建并写入数据
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "data.xlsx"
});
var sheet = excel.sheet("数据表");
if (!sheet) {
sheet = excel.createSheet("数据表");
}
sheet.createHeader(["姓名", "年龄", "分数", "是否通过"]);
sheet.insert({
姓名: "张三",
年龄: 25,
分数: 90,
是否通过: true
});
sheet.insert([
{ 姓名: "李四", 年龄: 30, 分数: 85, 是否通过: false },
{ 姓名: "王五", 年龄: 22, 分数: 76, 是否通过: true }
]);
excel.save();
excel.close();查询和更新数据
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "data.xlsx"
});
var sheet = excel.sheet("数据表");
if (!sheet) {
console.log("❌ 数据表不存在");
excel.close();
exit();
}
// 如果文件已存在且包含表头行(之前已创建),需要设置表头行索引
// 否则查询条件中的列名(如"姓名")会被视为列字母,导致查询错误
sheet.setHeaderRow(0);
var cursor = sheet.query();
console.log("总行数:", cursor.count());
var cursor2 = sheet.query("年龄 > 20");
console.log("年龄 > 20 的行数:", cursor2.count());
var cursor3 = sheet.query("年龄 >= 20");
while (cursor3.moveToNext()) {
var row = cursor3.pick();
console.log(row.姓名, row.年龄, row.分数);
}
cursor3.close();
var updated = sheet.query("姓名 = '张三'").update({ 分数: 95 });
console.log("更新了", updated, "行");
var deleted = sheet.query("分数 < 60").delete();
console.log("删除了", deleted, "行");
var range = sheet.query("年龄 > 20").range();
console.log("匹配行范围:", range.address());
excel.save();
excel.close();行操作
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "row_ops.xlsx"
});
var sheet = excel.sheet("行操作");
if (!sheet) {
sheet = excel.createSheet("行操作");
}
sheet.write([
["姓名", "年龄", "分数"],
["张三", 25, 90],
["李四", 30, 85],
["王五", 22, 76]
]);
var row = sheet.row(1);
console.log("行索引:", row.index());
console.log("单元格数:", row.cellCount());
console.log("行高:", row.height());
row.setHeight(30);
row.style()
.bold(true)
.fontColor("#FF0000")
.background("#FFFF00")
.border("thin");
var newRow = sheet.row(10);
row.copy(newRow);
console.log("复制后新行值:", JSON.stringify(newRow.value()));
var pasteRow = sheet.row(12);
row.paste(pasteRow);
console.log("粘贴后值:", JSON.stringify(pasteRow.value()));
row.setHidden(true);
console.log("行是否隐藏:", row.hidden());
var insertRow = sheet.row(4);
insertRow.setValue(["插入", "测试", "行"]);
insertRow.insert();
var delRow = sheet.row(6);
delRow.remove();
excel.save();
excel.close();单元格操作
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "cell_ops.xlsx"
});
var sheet = excel.sheet("单元格操作");
if (!sheet) {
sheet = excel.createSheet("单元格操作");
}
sheet.range("A1:E5").setValue([
["姓名", "年龄", "分数", "日期", "是否通过"],
["张三", 25, 90.5, "2024-01-15", true],
["李四", 30, 85.0, "2024-02-20", false],
["王五", 22, 76.5, "2024-03-10", true],
["赵六", 28, 92.0, "2024-04-05", false]
]);
var cell = sheet.cell("B2");
console.log("值:", cell.value());
console.log("类型:", cell.type());
if (cell.type() === "NUMERIC") {
console.log("数字:", cell.number());
}
cell.style()
.bold(true)
.fontColor("#FF0000")
.background("#FFFF00")
.border("thin");
var formulaCell = sheet.cell("C6");
formulaCell.setFormula("SUM(C2:C5)");
console.log("公式:", formulaCell.formula());
console.log("计算结果:", formulaCell.calculatedValue());
var linkCell = sheet.cell("A10");
linkCell.setValue("点击访问");
linkCell.setHyperlink("https://example.com");
console.log("是否有超链接:", linkCell.hasHyperlink());
var link = linkCell.hyperlink();
if (link) {
console.log("地址:", link.address());
console.log("类型:", link.type());
}
var commentCell = sheet.cell("B10");
commentCell.setValue("批注单元格");
// 先移除已有批注(如果存在)
if (commentCell.hasComment()) {
commentCell.removeComment();
}
// 再设置新批注
commentCell.setComment("这是批注内容", "这是作者");
console.log("是否有批注:", commentCell.hasComment());
var comment = commentCell.comment();
if (comment) {
console.log("批注内容:", comment.text());
console.log("作者:", comment.author());
}
var dropCell = sheet.cell("C10");
dropCell.setValue("性别");
dropCell.setDropdown(["男", "女", "其他"]);
var intCell = sheet.cell("D10");
intCell.setInteger({
min: 18,
max: 60,
promptTitle: "年龄输入",
promptText: "请输入18-60之间的整数",
errorTitle: "错误",
errorText: "年龄必须在18-60之间"
});
var imgPath = "/sdcard/logo.png";
if (files.isFile(imgPath)) {
sheet.cell("E10").setImage(imgPath)
.size(200, 150)
.insert();
}
excel.save();
excel.close();样式和格式设置
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "style.xlsx"
});
var sheet = excel.sheet("样式测试");
if (!sheet) {
sheet = excel.createSheet("样式测试");
}
sheet.write([
["产品", "销量", "单价", "总价"],
["A", 100, 10.5, 1050],
["B", 200, 8.2, 1640],
["C", 150, 12.0, 1800]
]);
sheet.range("A1:D1").style()
.bold(true)
.fontColor("#FFFFFF")
.background("#4472C4")
.align("center")
.border("thin");
sheet.range("A2:D4").style()
.border("thin")
.align("center")
.vertical("middle");
sheet.range("B2:B4").style().format("#,##0");
sheet.range("C2:C4").style().format("0.00");
sheet.range("D2:D4").style().format("#,##0.00");
sheet.cell("E2").setValue(0.85);
sheet.cell("E2").style().format("0.00%");
sheet.cell("E3").setValue("2024-01-15");
sheet.cell("E3").style().format("yyyy年MM月dd日");
sheet.setColumnWidth(0, 12);
sheet.setColumnWidth(1, 15);
sheet.setColumnWidth(2, 12);
sheet.setColumnWidth(3, 15);
excel.save();
excel.close();数据验证
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "validation.xlsx"
});
var sheet = excel.sheet("验证测试");
if (!sheet) {
sheet = excel.createSheet("验证测试");
}
sheet.createHeader(["字段", "验证规则", "示例值"]);
sheet.cell(1, 0).setValue("性别");
sheet.cell(1, 2).setDropdown(["男", "女", "其他"]);
sheet.cell(2, 0).setValue("年龄");
sheet.cell(2, 2).setInteger({
min: 18,
max: 60,
promptTitle: "年龄输入",
promptText: "请输入18-60之间的整数",
errorTitle: "错误",
errorText: "年龄必须在18-60之间"
});
sheet.cell(3, 0).setValue("分数");
sheet.cell(3, 2).setDecimal({
min: 0.0,
max: 100.0,
promptTitle: "分数输入",
promptText: "请输入0-100之间的数值",
errorTitle: "错误",
errorText: "分数必须在0-100之间"
});
sheet.cell(4, 0).setValue("日期");
sheet.cell(4, 2).setDate({
min: "2020-01-01",
max: "2025-12-31",
promptTitle: "日期输入",
promptText: "请输入2020-2025之间的日期",
errorTitle: "错误",
errorText: "日期超出范围"
});
sheet.cell(5, 0).setValue("时间");
sheet.cell(5, 2).setTime({
min: "08:00",
max: "18:00",
promptTitle: "时间输入",
promptText: "请输入08:00到18:00之间的时间",
errorTitle: "错误",
errorText: "时间超出范围"
});
sheet.cell(6, 0).setValue("文本");
sheet.cell(6, 2).setTextLength({
minLength: 5,
maxLength: 10,
promptTitle: "文本输入",
promptText: "请输入5-10个字符",
errorTitle: "错误",
errorText: "长度必须在5-10之间"
});
var validations = sheet.validations();
console.log("验证数量:", validations.size());
validations.forEach(function(v) {
console.log("类型:", v.type(), "区域:", JSON.stringify(v.regions()));
});
excel.save();
excel.close();合并单元格和边框
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "merge.xlsx"
});
var sheet = excel.sheet("合并测试");
if (!sheet) {
sheet = excel.createSheet("合并测试");
}
sheet.write([
["季度", "Q1", "Q2", "Q3", "Q4"],
["2023", 100, 120, 110, 130],
["2024", 140, 150, 135, 160]
]);
sheet.range("A1:E1").style()
.bold(true)
.background("#4472C4")
.fontColor("#FFFFFF")
.align("center");
sheet.range("A2:A3").merge();
sheet.range("A2:A3").style()
.bold(true)
.align("center")
.vertical("middle")
.border("thin");
sheet.range("B2:E3").style()
.border("thin")
.align("center");
sheet.cell(4, 0).setValue("合计");
sheet.cell(4, 1).setFormula("SUM(B2:B3)");
sheet.cell(4, 2).setFormula("SUM(C2:C3)");
sheet.cell(4, 3).setFormula("SUM(D2:D3)");
sheet.cell(4, 4).setFormula("SUM(E2:E3)");
sheet.range("A4:E4").style()
.bold(true)
.background("#E6E6E6")
.border("thin");
sheet.range("A4:A4").isMerged();
if (sheet.range("A1:B1").hasMergedOverlap()) {
sheet.range("A1:B1").clearMergedOverlap();
}
var merged = sheet.mergedRegions();
console.log("合并区域数:", merged.size());
merged.forEach(function(range) {
console.log("合并区域:", range.formatAsString());
});
excel.save();
excel.close();插入图片
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "image.xlsx"
});
var sheet = excel.sheet("图片测试");
if (!sheet) {
sheet = excel.createSheet("图片测试");
}
sheet.write([
["图片示例", "说明"],
["Logo", "插入到 A1 单元格"],
["图表", "插入到 C5 单元格"]
]);
var imgPath1 = "/sdcard/logo.png";
if (files.isFile(imgPath1)) {
sheet.image(imgPath1)
.at("A1")
.size(200, 150)
.insert();
console.log("✅ Logo 已插入到 A1");
}
var imgPath2 = "/sdcard/chart.png";
if (files.isFile(imgPath2)) {
sheet.image(imgPath2)
.at(4, 2)
.width(300)
.height(200)
.insert();
console.log("✅ 图表已插入到 E5");
}
var imgPath3 = "/sdcard/photo.jpg";
if (files.isFile(imgPath3)) {
var cell = sheet.cell("A10");
cell.setValue("照片");
cell.setImage(imgPath3)
.size(150, 150)
.insert();
console.log("✅ 照片已插入到 A10");
}
var imgPath4 = "/sdcard/icon.png";
if (files.isFile(imgPath4)) {
var bytes = files.readBytes(imgPath4);
sheet.image(bytes)
.at("G1")
.size(100, 100)
.insert();
console.log("✅ 图标已插入到 G1");
}
excel.save();
excel.close();筛选、冻结和保护
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "ffp.xlsx"
});
var sheet = excel.sheet("FFP测试");
if (!sheet) {
sheet = excel.createSheet("FFP测试");
}
sheet.write([
["姓名", "部门", "工资", "入职日期"],
["张三", "技术部", 8000, "2023-01-15"],
["李四", "市场部", 9000, "2022-11-01"],
["王五", "技术部", 7500, "2023-03-20"],
["赵六", "财务部", 8500, "2022-08-10"],
["孙七", "市场部", 7800, "2023-02-28"],
["周八", "技术部", 8200, "2023-04-05"],
["吴九", "财务部", 8800, "2022-12-11"]
]);
sheet.range("A1:D1").style()
.bold(true)
.background("#4472C4")
.fontColor("#FFFFFF")
.align("center");
sheet.autoFilter("A1:D9");
console.log("是否有自动筛选:", sheet.hasAutoFilter());
console.log("筛选范围:", sheet.autoFilterRange());
sheet.freeze(1, 0);
console.log("已冻结首行");
sheet.setProtect("password123");
console.log("工作表已保护");
sheet.setProtect();
console.log("已取消保护");
sheet.range("A2:D9").sort(2, false);
console.log("已按工资降序排序");
sheet.unfreeze();
console.log("已取消冻结");
excel.save();
excel.close();完整工作流(综合示例)
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "workflow.xlsx"
}, {
onOpen: function(excel) {
console.log("✅ 文件打开成功");
}
});
try {
var sheet = excel.sheet("工作流");
if (!sheet) {
sheet = excel.createSheet("工作流");
console.log("✅ 创建新 Sheet: 工作流");
}
sheet.createHeader(["ID", "任务", "负责人", "状态", "完成日期"]);
console.log("✅ 表头已创建");
sheet.insert([
{ ID: 1, 任务: "需求分析", 负责人: "张三", 状态: "已完成", 完成日期: "2024-01-15" },
{ ID: 2, 任务: "系统设计", 负责人: "李四", 状态: "进行中", 完成日期: "" },
{ ID: 3, 任务: "编码实现", 负责人: "王五", 状态: "未开始", 完成日期: "" },
{ ID: 4, 任务: "测试验证", 负责人: "赵六", 状态: "未开始", 完成日期: "" }
]);
console.log("✅ 数据已插入");
sheet.range("A1:E1").style()
.bold(true)
.background("#4472C4")
.fontColor("#FFFFFF")
.align("center")
.border("thin");
console.log("✅ 表头样式已应用");
for (var i = 2; i <= 5; i++) {
sheet.cell(i, 3).setDropdown(["未开始", "进行中", "已完成", "已取消"]);
}
console.log("✅ 状态列数据验证已添加");
var cursor = sheet.query("状态 = '已完成'");
cursor.ranges().style()
.background("#C6EFCE")
.fontColor("#006100");
var cursor2 = sheet.query("状态 = '进行中'");
cursor2.ranges().style()
.background("#FFEB9C")
.fontColor("#9C6500");
var cursor3 = sheet.query("状态 = '未开始'");
cursor3.ranges().style()
.background("#FFC7CE")
.fontColor("#9C0006");
console.log("✅ 状态颜色已应用");
sheet.autoFilter("A1:E5");
console.log("✅ 自动筛选已设置");
sheet.freeze(1, 0);
console.log("✅ 表头已冻结");
var cursor4 = sheet.query();
console.log("📊 总记录数:", cursor4.count());
excel.save();
console.log("💾 保存成功");
} catch (e) {
console.log("❌ 错误:", e);
} finally {
excel.close();
console.log("🔒 文件已关闭");
}范围(Range)高级操作
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "range_advanced.xlsx"
});
var sheet = excel.sheet("Range高级");
if (!sheet) {
sheet = excel.createSheet("Range高级");
}
sheet.write([
["产品", "销量", "单价", "总价"],
["A", 100, 10.5, 1050],
["B", 200, 8.2, 1640],
["C", 150, 12.0, 1800]
]);
var range = sheet.range("A1:D4");
console.log("范围地址:", range.address());
console.log("行数:", range.height());
console.log("列数:", range.width());
console.log("单元格数:", range.cellCount());
console.log("是否为空:", range.empty());
var singleCell = sheet.range("B2");
console.log("B2 值:", singleCell.value());
sheet.range("F1:G3").setValue([
["复制", "区域"],
["A", "B"],
["C", "D"]
]);
var src = sheet.range("A1:D1");
var tgt = sheet.range("A6:D6");
src.copy(tgt);
console.log("复制后目标值:", JSON.stringify(tgt.value()));
var pasteVal = sheet.range("A8:D8");
src.paste(pasteVal);
console.log("粘贴值后:", JSON.stringify(pasteVal.value()));
var srcStyle = sheet.range("A1:D1").style().bold(true).background("#4472C4");
var tgtStyle = sheet.range("A10:D10");
src.pasteStyle(tgtStyle);
console.log("样式已粘贴");
var r6 = sheet.range("A12:B13");
console.log("原始地址:", r6.address());
var offset = r6.offset(2, 3);
console.log("偏移后地址:", offset.address());
var resize = r6.resize(4, 5);
console.log("调整大小后地址:", resize.address());
var r7a = sheet.range("A15:C18");
var r7b = sheet.range("B16:D19");
var inter = r7a.intersect(r7b);
console.log("交集:", inter ? inter.address() : "无交集");
var union = r7a.union(r7b);
console.log("并集:", union ? union.address() : "不相邻");
var r8 = sheet.range("A20:C22");
r8.setValue([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]);
// 遍历每个单元格
for (var r = 0; r < r8.height(); r++) {
for (var c = 0; c < r8.width(); c++) {
var cell = r8.cell(r, c);
console.log("单元格[" + r + "][" + c + "]值:", cell.value());
}
}
excel.save();
excel.close();查询结果范围(Cursor Ranges)
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "cursor_ranges.xlsx"
});
var sheet = excel.sheet("查询范围");
if (!sheet) {
sheet = excel.createSheet("查询范围");
}
sheet.createHeader(["姓名", "年龄", "班级"]);
sheet.insert([
{ 姓名: "张三", 年龄: 18, 班级: "一班" },
{ 姓名: "李四", 年龄: 19, 班级: "二班" },
{ 姓名: "王五", 年龄: 20, 班级: "一班" },
{ 姓名: "赵六", 年龄: 21, 班级: "三班" },
{ 姓名: "孙七", 年龄: 22, 班级: "二班" },
{ 姓名: "周八", 年龄: 23, 班级: "三班" }
]);
var cursor = sheet.query("班级 = '一班' OR 班级 = '三班'");
var ranges = cursor.ranges();
console.log("连续块数量:", ranges.size());
ranges.forEach(function(range) {
console.log("块地址:", range.address());
});
ranges.style()
.bold(true)
.background("#FFFF00")
.border("thin");
var singleRange = cursor.range();
console.log("合并范围地址:", singleRange.address());
singleRange.style()
.fontColor("#FF0000")
.border("medium");
var rows = cursor.rows();
console.log("匹配行数:", rows.size());
rows.forEach(function(row) {
console.log("行索引:", row.index());
});
var indexes = cursor.rowIndexes();
console.log("行索引:", JSON.stringify(indexes));
console.log("使用 size/get 遍历:");
for (var i = 0; i < ranges.size(); i++) {
var r = ranges.get(i);
console.log("块", i, ":", r.address());
}
excel.save();
excel.close();无表头模式操作
var TEST_DIR = "/sdcard/ExcelTest/";
files.ensureDir(TEST_DIR);
var excel = $excel.open({
path: TEST_DIR + "noheader.xlsx"
});
var sheet = excel.sheet("无表头测试");
if (!sheet) {
sheet = excel.createSheet("无表头测试");
}
sheet.noHeader();
sheet.insert({ "A": "姓名", "B": "年龄", "C": "分数" });
sheet.insert([
{ "A": "张三", "B": 18, "C": 90 },
{ "A": "李四", "B": 19, "C": 85 },
{ "A": "王五", "B": 20, "C": 78 }
]);
var cursor = sheet.query();
console.log("所有数据:", JSON.stringify(cursor.all(), null, 2));
var cursor2 = sheet.query("B > 18");
console.log("年龄 > 18 的数据:", JSON.stringify(cursor2.all(), null, 2));
var updated = sheet.query("A = '张三'").update({ "C": 95 });
console.log("更新了", updated, "行");
var columns = sheet.columns();
console.log("列名:", JSON.stringify(columns));
sheet.write([
["产品", "价格"],
["苹果", 5.5],
["香蕉", 3.2]
]);
excel.save();
excel.close();