在 React 中通过 JavaScript 实现 Excel 单元格、行与列的锁定保护

在 Web 应用开发中,处理 Excel 文件时常常需要对关键数据进行保护。例如,当某些公式、表头或参照值必须保持不变以确保数据完整性时,通过锁定特定单元格、行或列,可以有效防止意外修改,控制用户对电子表格内容的编辑权限。本文将介绍如何在 React 框架中,利用 Spire.XLS for JavaScript 这一基于 WebAssembly 的前端库,实现 Excel 单元格、行和列的锁定功能。

该库的一个特点是独立于 Microsoft Office 环境运行,可以在浏览器或 Node.js 环境中处理 Excel 文档,所有操作在本地完成,不依赖云服务。

环境配置

在 React 项目中开始之前,需要通过 npm 安装该库:

bash 复制代码
npm i spire.office

安装完成后,需要将库相关的 JavaScript 文件(如 spire.xls.js)和 WebAssembly 文件(.wasm)放置在项目的 public 目录下,以便在浏览器中动态加载。

在 React 中加载 WASM 模块

由于该库基于 WebAssembly,需要在 React 组件挂载时异步加载模块。以下是一种常见的加载方式:

jsx 复制代码
import React, { useState, useEffect } from 'react';

function App() {
  const [wasmModule, setWasmModule] = useState(null);

  useEffect(() => {
    (async () => {
      try {
        const publicUrl = process.env.PUBLIC_URL || '';
        const spireModule = await import(/* webpackIgnore: true */ `${publicUrl}/spire.xls.js`);
        const rawModule = spireModule.default || spireModule;
        window.wasmModule = typeof rawModule === 'function'
          ? await rawModule({ locateFile: p => p.endsWith('.wasm') ? `${publicUrl}/${p}` : p })
          : rawModule;
        setWasmModule(window.wasmModule);
      } catch (error) {
        console.error('Failed to load spire.xls.js WASM module:', error);
      }
    })();
  }, []);

  // 后续操作...
}

该方式通过动态导入加载库文件,并在 WebAssembly 运行时初始化完成后,将模块实例保存到组件状态中,供后续操作使用。

锁定特定单元格

Excel 默认情况下,工作表中所有单元格的锁定属性为 true,但只有在工作表被保护(Protect)后,锁定才会生效。因此,锁定特定单元格的核心步骤是:

  1. 先将工作表中所有单元格 的锁定属性设为 false(解锁)。
  2. 再将需要保护的特定单元格或区域 的锁定属性设为 true
  3. 调用 Worksheet.Protect() 方法启用工作表保护,并设置密码(可选)。

以下是一个完整的示例代码,演示如何锁定 A1 单元格以及 C1:E3 区域:

jsx 复制代码
const LockExcelCells = async () => {
  const wasmModule = window.wasmModule.spirexls;

  if (wasmModule) {
    // 1. 加载字体文件到虚拟文件系统(VFS),确保文本正常渲染
    await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);

    // 2. 加载待处理的 Excel 文件到 VFS
    const inputFileName = '示例.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

    // 3. 创建工作簿实例并加载文件
    const workbook = new wasmModule.Workbook();
    workbook.LoadFromFile(inputFileName);

    // 4. 获取第一个工作表
    let sheet = workbook.Worksheets.get(0);

    // 5. 解锁工作表中已使用范围内的所有单元格
    sheet.Range.Style.Locked = false;

    // 6. 锁定特定单元格 A1
    sheet.Range.get("A1").Style.Locked = true;

    // 7. 锁定特定区域 C1:E3
    sheet.Range.get("C1:E3").Style.Locked = true;

    // 8. 使用密码保护工作表
    sheet.Protect({ password: "123", options: wasmModule.SheetProtectionType.All });

    // 9. 保存结果文件
    const outputFileName = "锁定单元格.xlsx";
    workbook.SaveToFile({ fileName: outputFileName, version: wasmModule.ExcelVersion.Version2010 });

    // 10. 从 VFS 读取文件并触发下载
    const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const modifiedFile = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(modifiedFile);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);

    // 11. 释放资源
    workbook.Dispose();
  }
};

该方法通过先全部解锁、再单独锁定的方式,实现了对特定单元格的保护。SheetProtectionType.All 选项表示限制用户对工作表的所有编辑操作,包括插入、删除、格式调整等。

锁定整行

如果需要锁定整行,可以通过 Rows.get() 方法按索引来指定行。索引从 0 开始,例如 Rows.get(2) 表示第三行:

jsx 复制代码
const LockExcelRows = async () => {
  const wasmModule = window.wasmModule.spirexls;

  if (wasmModule) {
    await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
    
    const inputFileName = '示例.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

    const workbook = new wasmModule.Workbook();
    workbook.LoadFromFile(inputFileName);

    let sheet = workbook.Worksheets.get(0);

    // 解锁所有单元格
    sheet.Range.Style.Locked = false;

    // 锁定第三行(索引从0开始)
    sheet.Rows.get(2).Style.Locked = true;

    sheet.Protect({ password: "123", options: wasmModule.SheetProtectionType.All });

    const outputFileName = "锁定行.xlsx";
    workbook.SaveToFile({ fileName: outputFileName, version: wasmModule.ExcelVersion.Version2010 });

    const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const modifiedFile = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(modifiedFile);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);

    workbook.Dispose();
  }
};

锁定整列

如果需要锁定整列,可以通过 Columns.get() 方法按索引来指定列。索引同样从 0 开始,例如 Columns.get(1) 表示第二列(B列):

jsx 复制代码
const LockExcelColumns = async () => {
  const wasmModule = window.wasmModule.spirexls;

  if (wasmModule) {
    await window.spire.FetchFileToVFS('Arial.ttf', '/Library/Fonts/', `${process.env.PUBLIC_URL}/static/font/`);
    
    const inputFileName = '示例.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

    const workbook = new wasmModule.Workbook();
    workbook.LoadFromFile(inputFileName);

    let sheet = workbook.Worksheets.get(0);

    // 解锁所有单元格
    sheet.Range.Style.Locked = false;

    // 锁定第四列(索引从0开始)
    sheet.Columns.get(1).Style.Locked = true;

    sheet.Protect({ password: "123", options: wasmModule.SheetProtectionType.All });

    const outputFileName = "锁定列.xlsx";
    workbook.SaveToFile({ fileName: outputFileName, version: wasmModule.ExcelVersion.Version2010 });

    const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const modifiedFile = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(modifiedFile);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);

    workbook.Dispose();
  }
};

注意事项

  • 字体加载 :该库依赖系统字体来渲染文本内容,如果导出的 Excel 文件中出现文本显示异常,通常是因为缺少对应的字体文件。需要通过 FetchFileToVFS 预先加载字体到虚拟文件系统。
  • 虚拟文件系统(VFS) :所有加载和生成的文件都存储在 WebAssembly 的内存文件系统中,而非用户设备的物理磁盘。读取结果文件时,需要通过 window.dotnetRuntime.Module.FS.readFile 读取,再转换为浏览器可识别的 Blob 对象进行下载。
  • 版本差异 :不同版本的库在 API 调用方式上可能存在差异。根据官方文档,较新版本使用 spire.office 作为包名,并可能调整了部分方法的调用方式,建议参考对应版本的文档进行适配。
  • 授权说明:该库提供免费版本供开发测试使用,免费版可能会对文档页数或某些高级功能有所限制。在项目集成前,建议查阅其授权条款,确认是否符合使用场景。
  • 资源释放 :操作完成后,调用 workbook.Dispose() 有助于及时回收内存资源,避免内存泄漏。

总结

通过 Spire.XLS for JavaScript 提供的 Style.Locked 属性和 Worksheet.Protect() 方法,可以在 React 应用中较为简洁地实现 Excel 单元格、行和列的锁定保护。这种方法依托 WebAssembly 技术,在浏览器端直接完成操作,无需后端服务支持,适合对数据隐私和响应速度有一定要求的场景。实际使用时,需要留意字体加载、虚拟文件系统操作以及资源释放等细节。

相关推荐
এ慕ོ冬℘゜1 小时前
深入理解 JavaScript 事件对象:从 target 到 preventDefault 的实战指南
开发语言·前端·javascript
Listen·Rain1 小时前
Vue概述
前端·javascript·vue.js
小雪_Snow2 小时前
JS 数组的遍历 —— 两种方式详解
前端·javascript
小二·2 小时前
WebGPU 浏览器端跑大模型:让AI在网页里跑起来(WebLLM/Transformers.js实战)
开发语言·javascript·人工智能
心中有国也有家2 小时前
AtomGit Flutter 鸿蒙客户端:零外部资源的应用打包策略
android·javascript·flutter·华为·harmonyos
胡萝卜术11 小时前
从API调用到手写LRU:力扣146「LRU缓存」的哈希表+双向链表终极进化之路
前端·javascript·面试
科技道人11 小时前
记录 默认置灰/禁用 app ‘Search Engine Selector‘ 的disable按钮
开发语言·前端·javascript
shuaijie051817 小时前
强制修改调用接口的api地址。
javascript·vue.js·ecmascript
JerrySir17 小时前
恶意 npm 包只活了 17 分钟:技术面试里,为什么“升级依赖”还不算止血?
javascript·安全