目录
- [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)
}
到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕
