欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > [特殊字符] 使用 Handsontable 构建一个支持 Excel 公式计算的动态表格

[特殊字符] 使用 Handsontable 构建一个支持 Excel 公式计算的动态表格

2025/4/5 13:00:21 来源:https://blog.csdn.net/weixin_41644392/article/details/146983435  浏览:    关键词:[特殊字符] 使用 Handsontable 构建一个支持 Excel 公式计算的动态表格

在 Web 应用中,处理表格数据并提供 Excel 级的功能(如公式计算、数据导入导出)一直是个挑战。今天,我将带你使用 React + Handsontable 搭建一个强大的 Excel 风格表格,支持 公式计算Excel 文件导入导出,并实现 动态单元格样式

🎯 项目目标

  • ✅ 创建一个可编辑的 Excel 风格表格

  • ✅ 支持 Excel 公式解析(如 =B1+10

  • ✅ 支持 Excel 文件导入/导出(.xlsx/.xls)

  • ✅ 实现 单元格动态渲染(如公式高亮)

📦 依赖安装

首先,确保你的 React 项目已创建(若没有,可用 npx create-react-app my-app 创建)。然后,安装必要的依赖项:

npm install @handsontable/react handsontable mathjs xlsx react-i18next

📌 核心代码解析

1️⃣ 创建 Excel 风格的 Handsontable 表格

import React, { useState } from 'react';
import { HotTable } from '@handsontable/react';
import { useTranslation } from 'react-i18next';
import * as math from 'mathjs';
import * as XLSX from 'xlsx';
import 'handsontable/dist/handsontable.full.min.css';const ExcelTable = () => {const { t } = useTranslation();const [data, setData] = useState([[t('name'), t('age'), t('city'), 'Total'],['John', 30, 'New York', '=B1+10'],['Alice', 25, 'London', '=B2*2'],]);
  • useState 初始化数据,支持 公式输入(如 =B1+10

  • Handsontable 是一个轻量级但功能强大的表格库,支持 Excel 风格的操作


2️⃣ 实现公式计算功能

const calculateFormula = (value, row, col, dataArray) => {if (typeof value === 'string' && value.startsWith('=')) {try {const formula = value.slice(1).replace(/[A-Z]\d+/g, (cell) => {const colLetter = cell.match(/[A-Z]/)[0];const rowNum = parseInt(cell.match(/\d+/)[0], 10) - 1;const colNum = colLetter.charCodeAt(0) - 65;return dataArray[rowNum][colNum];});return math.evaluate(formula);} catch (e) {return '#ERROR';}}return value;
};
  • 解析 Excel 格式的公式=B1+10

  • 将公式转换为 数学计算表达式 并使用 math.evaluate() 计算结果

  • 错误处理:如果解析失败,返回 #ERROR


3️⃣ 实现 Excel 文件导入功能

const handleImport = (event) => {const file = event.target.files[0];const reader = new FileReader();reader.onload = (e) => {const binaryStr = e.target.result;const workbook = XLSX.read(binaryStr, { type: 'binary' });const sheetName = workbook.SheetNames[0];const sheet = workbook.Sheets[sheetName];const importedData = XLSX.utils.sheet_to_json(sheet, { header: 1 });setData(importedData);};reader.readAsBinaryString(file);
};
  • 用户选择 Excel 文件

  • 解析 Excel 数据 并转换为 JavaScript 数组

  • 更新 Handsontable 的 data 以渲染新数据


4️⃣ 实现 Excel 文件导出功能

const handleExport = () => {const ws = XLSX.utils.aoa_to_sheet(data);const wb = XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');XLSX.writeFile(wb, 'exported_excel.xlsx');
};
  • 将 Handsontable 数据 转换为 Excel sheet

  • 创建新的 Excel 工作簿

  • 下载 Excel 文件,实现 导出功能


5️⃣ 动态单元格渲染(公式高亮)

<HotTabledata={data}rowHeaders={true}colHeaders={true}contextMenu={true}stretchH="all"beforeChange={(changes) => {changes.forEach(([row, col, , newValue]) => {data[row][col] = newValue;});setData([...data]);}}afterGetCellMeta={(row, col, cellProperties) => {const value = data[row][col];if (typeof value === 'string' && value.startsWith('=')) {cellProperties.readOnly = false;cellProperties.renderer = (instance, td, r, c, prop, val) => {const result = calculateFormula(val, r, c, data);td.innerHTML = result;td.style.backgroundColor = '#e0f7fa'; // 公式单元格高亮};} else {cellProperties.renderer = (instance, td, r, c, prop, val) => {td.innerHTML = val;td.style.backgroundColor = '#ffffff'; // 普通单元格白色背景};}}}cells={(row, col) => {const cellProperties = {};if (row === 0) {cellProperties.className = 'header-cell'; // 表头样式}return cellProperties;}}licenseKey="non-commercial-and-evaluation"
/>
  • 检测单元格是否包含公式

  • 动态渲染公式计算结果

  • 高亮公式单元格 以增强用户体验


 完整代码

// src/components/ExcelTable.jsx
import React, { useState } from 'react';
import { HotTable } from '@handsontable/react';
import { useTranslation } from 'react-i18next';
import * as math from 'mathjs';
import * as XLSX from 'xlsx';
import 'handsontable/dist/handsontable.full.min.css';const ExcelTable = () => {const { t } = useTranslation();const [data, setData] = useState([[t('name'), t('age'), t('city'), 'Total'],['John', 30, 'New York', '=B1+10'],['Alice', 25, 'London', '=B2*2'],]);// 计算公式const calculateFormula = (value, row, col, dataArray) => {if (typeof value === 'string' && value.startsWith('=')) {try {const formula = value.slice(1).replace(/[A-Z]\d+/g, (cell) => {const colLetter = cell.match(/[A-Z]/)[0];const rowNum = parseInt(cell.match(/\d+/)[0], 10) - 1;const colNum = colLetter.charCodeAt(0) - 65;return dataArray[rowNum][colNum];});return math.evaluate(formula);} catch (e) {return '#ERROR';}}return value;};// 导入 Excel 文件const handleImport = (event) => {const file = event.target.files[0];const reader = new FileReader();reader.onload = (e) => {const binaryStr = e.target.result;const workbook = XLSX.read(binaryStr, { type: 'binary' });const sheetName = workbook.SheetNames[0];const sheet = workbook.Sheets[sheetName];const importedData = XLSX.utils.sheet_to_json(sheet, { header: 1 });setData(importedData);};reader.readAsBinaryString(file);};// 导出 Excel 文件const handleExport = () => {const ws = XLSX.utils.aoa_to_sheet(data);const wb = XLSX.utils.book_new();XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');XLSX.writeFile(wb, 'exported_excel.xlsx');};return (<div><div style={{ marginBottom: '10px' }}><input type="file" accept=".xlsx, .xls" onChange={handleImport} /><button onClick={handleExport}>Export to Excel</button></div><HotTabledata={data}rowHeaders={true}colHeaders={true}contextMenu={true}stretchH="all"beforeChange={(changes) => {changes.forEach(([row, col, , newValue]) => {data[row][col] = newValue;});setData([...data]);}}afterGetCellMeta={(row, col, cellProperties) => {const value = data[row][col];if (typeof value === 'string' && value.startsWith('=')) {cellProperties.readOnly = false;cellProperties.renderer = (instance, td, r, c, prop, val) => {const result = calculateFormula(val, r, c, data);td.innerHTML = result;td.style.backgroundColor = '#e0f7fa'; // 公式单元格高亮};} else {cellProperties.renderer = (instance, td, r, c, prop, val) => {td.innerHTML = val;td.style.backgroundColor = '#ffffff'; // 普通单元格白色背景};}}}cells={(row, col) => {const cellProperties = {};if (row === 0) {cellProperties.className = 'header-cell'; // 表头样式}return cellProperties;}}licenseKey="non-commercial-and-evaluation"/><style jsx>{.header-cell {background-color: #f0f0f0;font-weight: bold;}}</style></div>);
};export default ExcelTable; 

🎉 运行效果

🚀 你现在拥有了一个 功能强大的 Excel 风格表格,支持:
公式计算(自动计算 =B1+10
Excel 文件导入/导出
动态高亮公式单元格
行列可编辑 & 右键菜单操作

💡 总结

通过 Handsontable + mathjs + xlsx,我们轻松构建了一个 Excel 风格的动态表格。这一方案适用于 财务管理、数据分析、在线表单应用 等场景,提升了数据处理的灵活性!

🔹 完整代码已上传 GitHub(可在评论区留言获取)
🔹 你对这个 Excel 组件有什么优化建议?欢迎评论交流!

🔥 如果觉得有帮助,别忘了点赞 + 收藏! 🎯

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词