前端 Excel 工具组件实战:导入 → 可编辑表格 → 导出 + 样式同步 + 单元格合并

目录

  • [1. 项目初始化与依赖安装](#1. 项目初始化与依赖安装)
  • [2. 表格组件设计(基于 Handsontable)](#2. 表格组件设计(基于 Handsontable))
  • [3. 导入功能:支持 `.xlsx` / `.csv`](#3. 导入功能:支持 .xlsx / .csv)
  • [4. 导出功能:带样式 & 合并信息](#4. 导出功能:带样式 & 合并信息)
  • [5. 样式同步实现方案](#5. 样式同步实现方案)
  • [6. 合并单元格功能逻辑](#6. 合并单元格功能逻辑)
  • [7. 拓展功能建议 + 示例代码](#7. 拓展功能建议 + 示例代码)
    • [✅ 设置单元格背景色按钮](#✅ 设置单元格背景色按钮)
    • [✅ 支持 JSON 数据导出保存](#✅ 支持 JSON 数据导出保存)

1. 项目初始化与依赖安装

bash 复制代码
npm init vite@latest excel-editor --template vue-ts  
cd excel-editor  
npm install  
npm install xlsx file-saver handsontable @handsontable/vue  
npm install -D tailwindcss postcss autoprefixer  
npx tailwindcss init -p  

main.ts 中引入 Tailwind:

ts 复制代码
import './index.css'  

2. 表格组件设计(基于 Handsontable)

我们封装一个 ExcelEditor.vue 组件,使用 @handsontable/vue

ts 复制代码
<template>  
  <hot-table  
    ref="hotTableRef"  
    :settings="settings"  
    licenseKey="non-commercial-and-evaluation"  
  />  
</template>  

<script setup lang="ts">  
import { ref } from 'vue'  
import { HotTable } from '@handsontable/vue'  
import Handsontable from 'handsontable'  

const hotTableRef = ref()  
const settings = {  
  data: Handsontable.helper.createEmptySpreadsheetData(20, 10),  
  rowHeaders: true,  
  colHeaders: true,  
  contextMenu: true,  
  mergeCells: [],  
  manualRowResize: true,  
  manualColumnResize: true,  
  comments: true,  
  cells(row: number, col: number) {  
    return { className: '' }  
  }  
}  
</script>  

3. 导入功能:支持 .xlsx / .csv

ts 复制代码
import * as XLSX from 'xlsx'  

const handleFileUpload = (file: File) => {  
  const reader = new FileReader()  
  reader.onload = (e) => {  
    const data = new Uint8Array(e.target!.result as ArrayBuffer)  
    const workbook = XLSX.read(data, { type: 'array' })  
    const sheet = workbook.Sheets[workbook.SheetNames[0]]  
    const json = XLSX.utils.sheet_to_json(sheet, { header: 1 })  
    hotTableRef.value.hotInstance.loadData(json)  
  }  
  reader.readAsArrayBuffer(file)  
}  

4. 导出功能:带样式 & 合并信息

ts 复制代码
import { saveAs } from 'file-saver'  

const exportToExcel = () => {  
  const hot = hotTableRef.value.hotInstance  
  const data = hot.getData()  
  const ws = XLSX.utils.aoa_to_sheet(data)  

  const merges = hot.getPlugin('mergeCells').mergedCellsCollection.mergedCells  
  ws['!merges'] = merges.map(m => ({  
    s: { r: m.row, c: m.col },  
    e: { r: m.row + m.rowspan - 1, c: m.col + m.colspan - 1 }  
  }))  

  const wb = XLSX.utils.book_new()  
  XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')  
  const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' })  
  saveAs(new Blob([wbout]), 'table.xlsx')  
}  

5. 样式同步实现方案

ts 复制代码
const cellStyles = new Map<string, any>()  

const updateCellStyle = (row: number, col: number, style: any) => {  
  const key = `${row}_${col}`  
  cellStyles.set(key, style)  
}  

cells(row, col) {  
  const key = `${row}_${col}`  
  const style = cellStyles.get(key) || {}  
  return {  
    renderer(hotInstance, td, row, col, prop, value) {  
      td.style.backgroundColor = style.backgroundColor || ''  
      td.style.fontWeight = style.fontWeight || ''  
      td.innerText = value  
      return td  
    }  
  }  
}  

6. 合并单元格功能逻辑

ts 复制代码
const mergeSelectedCells = () => {  
  const hot = hotTableRef.value.hotInstance  
  const selected = hot.getSelectedLast()  
  if (!selected) return  

  const [startRow, startCol, endRow, endCol] = selected  
  hot.getPlugin('mergeCells').merge({  
    row: Math.min(startRow, endRow),  
    col: Math.min(startCol, endCol),  
    rowspan: Math.abs(endRow - startRow) + 1,  
    colspan: Math.abs(endCol - startCol) + 1,  
  })  
  hot.render()  
}  

7. 拓展功能建议 + 示例代码

✅ 设置单元格背景色按钮

ts 复制代码
<button @click="setCurrentCellStyle({ backgroundColor: '#ffff99' })">  
  高亮当前单元格  
</button>  
  
const setCurrentCellStyle = (style: any) => {  
  const hot = hotTableRef.value.hotInstance  
  const [row, col] = hot.getSelectedLast() || []  
  updateCellStyle(row, col, style)  
  hot.render()  
 

✅ 支持 JSON 数据导出保存

ts 复制代码
const getJsonData = () => {  
  const data = hotTableRef.value.hotInstance.getData()  
  return JSON.stringify(data)  
}  

到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~

创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:

点个赞❤️ 让更多人看到优质内容

关注「前端极客探险家」🚀 每周解锁新技巧

收藏文章⭐️ 方便随时查阅

📢 特别提醒:

转载请注明原文链接,商业合作请私信联系

感谢你的阅读!我们下篇文章再见~ 💕

相关推荐
linux_cfan28 分钟前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
kyriewen43 分钟前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理1 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
IT_陈寒2 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端
Blanche15002 小时前
利用 RAG 为答疑机器人扩展知识范围
前端
天若有情6732 小时前
【纯前端小工具】公历生日转农历,批量查询每年农历生日对应的公历日期(GitHub Pages在线直接用)
前端·javascript·github pages·农历转换·lunisolar·网页小工具
SoonITer2 小时前
怎样构建一个 Agent-friendly 的网站
前端·agent
颜进强3 小时前
01 · NestJS 是什么:用途、解决什么问题、与热门框架对比
前端·后端·ai编程
wendZzoo3 小时前
前端工程师的 3D 第一课:一个模型如何进入网页
前端
颜进强3 小时前
04 · NestJS 依赖注入:你在 `@Module` 写的 providers,和构造参数里那个类型,是怎么"对上"的?
前端·后端·ai编程