Appearance
实现 Excel 互转 工具
分类:developer | 标签:Excel、xlsx、转换 Excel 与 JSON/CSV 互相转换
2.1 功能说明
Excel 与 JSON/CSV 互相转换
2.1.1 使用指南
功能说明
本地解析 Excel(.xlsx/.xls),多 Sheet 预览并转为 JSON / CSV;也可将 JSON 数组或 CSV 文本生成为 Excel 文件下载。全程在浏览器完成,不上传服务器。
使用场景
表格数据格式转换、接口数据 mock、报表导出。
2.2 代码实现
2.2.1 组件结构
本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-input、el-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。
2.2.2 核心逻辑概览
<script setup> 中定义的主要函数/方法:
onFile()renderSheet()copyOut()download()save()genExcel()
2.2.3 关键实现代码
以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。
vue
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import XLSX from '@/vendor/xlsx/xlsx.full.min.js'
import ToolPageShell from '@/components/ToolPageShell.vue'
const tab = ref('toJson')
const err = ref('')
const sheetNames = ref([])
const sheet = ref('')
const jsonOut = ref('')
const csvOut = ref('')
const preview = ref([])
const previewCols = ref([])
let wb = null
const inFmt = ref('json')
const inText = ref('')
const outSheet = ref('Sheet1')
function onFile(e) {
err.value = ''; sheetNames.value = []; jsonOut.value = ''; csvOut.value = ''
const f = e.target.files && e.target.files[0]
if (!f) return
if (!/\.(xlsx|xls)$/i.test(f.name)) { err.value = '请选择 .xlsx 或 .xls 文件'; return }
f.arrayBuffer().then(buf => {
wb = XLSX.read(new Uint8Array(buf), { type: 'array' })
sheetNames.value = wb.SheetNames
sheet.value = wb.SheetNames[0]
renderSheet()
}).catch(er => { err.value = '读取失败:' + er.message })
}
function renderSheet() {
if (!wb || !sheet.value) return
const ws = wb.Sheets[sheet.value]
const rows = XLSX.utils.sheet_to_json(ws, { defval: '', header: 1 })
jsonOut.value = JSON.stringify(XLSX.utils.sheet_to_json(ws, { defval: '' }), null, 2)
csvOut.value = XLSX.utils.sheet_to_csv(ws)
const head = (rows[0] || []).map((_, i) => '列' + (i + 1))
previewCols.value = head
preview.value = rows.slice(0, 50).map(r => {
const o = {}
head.forEach((_, i) => { o[i] = r[i] ?? '' })
return o
})
}
function copyOut(t) {
const v = t === 'json' ? jsonOut.value : csvOut.value
if (v) navigator.clipboard?.writeText(v).then(() => ElMessage.success('已复制'))
}
function download(t) {
if (t === 'json') {
const blob = new Blob([jsonOut.value], { type: 'application/json;charset=utf-8' })
save(blob, (sheet.value || 'sheet') + '.json')
} else {
const blob = new Blob(['\ufeff' + csvOut.value], { type: 'text/csv;charset=utf-8' })
save(blob, (sheet.value || 'sheet') + '.csv')
}
}
function save(blob, name) {
const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name; a.click(); URL.revokeObjectURL(a.href)
}
function genExcel() {
err.value = ''
const raw = inText.value.trim()
if (!raw) { err.value = '请输入数据'; return }
let aoa
try {
if (inFmt.value === 'json') {
const data = JSON.parse(raw)
if (!Array.isArray(data) || !data.length) throw new Error('需为对象数组')
const headers = [...new Set(data.flatMap(o => Object.keys(o)))]
aoa = [headers, ...data.map(o => headers.map(h => o[h] ?? ''))]
} else {
const lines = raw.split(/\r?\n/).filter(l => l.trim() !== '')
aoa = lines.map(l => l.split(','))
}
} catch (e) { err.value = '解析失败:' + e.message; return }
const ws = XLSX.utils.aoa_to_sheet(aoa)
const book = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(book, ws, outSheet.value || 'Sheet1')
const out = XLSX.write(book, { type: 'array', bookType: 'xlsx' })
save(new Blob([out], { type: 'application/octet-stream' }), 'export.xlsx')
ElMessage.success('已生成 Excel')
}2.3 效果截图
