Files
login_task_web/docs/excel导出.md

103 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

要真·生成 Excel.xlsx并避免中文乱码最简单稳妥的做法是用 **SheetJS (xlsx)**。xlsx 本身就是 Unicode不需要再加 BOM中文不会乱码。
### 1) 安装依赖
```bash
npm i xlsx
```
### 2) 在你的工具函数里引入并替换 `exportToExcel`
把你原来的 `exportToExcel`(写 `application/vnd.ms-excel` 的 HTML 方案)换成下面这个基于 xlsx 的实现——它会按 `headers` 的顺序与标题导出为真正的 `.xlsx` 文件,并自动设置列宽、表头冻结、筛选,同时做了“公式注入”防护。
```js
// 顶部添加
import { utils as XLSXUtils, writeFile as XLSXWriteFile } from 'xlsx'
// 计算列宽(中文按双字节处理)
function calcWch(val) {
const s = (val ?? '').toString()
let w = 0
for (const ch of s) w += ch.charCodeAt(0) > 255 ? 2 : 1
return Math.min(Math.max(w, 8), 60) // 8~60 字符宽
}
// 简单日期格式化(如果字段是 Date 或 ISO 字符串)
function fmtDate(v) {
const d = v instanceof Date ? v : (typeof v === 'string' && !isNaN(Date.parse(v)) ? new Date(v) : null)
if (!d) return v
const pad = n => (n < 10 ? '0' + n : '' + n)
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
}
// 真实 .xlsx 导出(中文不会乱码)
export function exportToExcel(data, headers, filename = '导出.xlsx', sheetName = 'Sheet1') {
// 1) 规范化数据:按 headers 顺序映射到“显示列名 -> 值”
const headerLabels = headers.map(h => h.label)
const rows = (data || []).map(row => {
const obj = {}
headers.forEach(h => {
let v = row?.[h.key]
// 识别并格式化日期
v = fmtDate(v)
// 复杂对象转字符串
if (v && typeof v === 'object' && !(v instanceof Date)) {
try { v = JSON.stringify(v) } catch { v = String(v) }
}
// 公式注入防护(以 = + - @ 开头的文本加前导单引号)
if (typeof v === 'string' && /^[=+\-@]/.test(v)) v = "'" + v
// null/undefined 转为空串,保留 0/false
if (v === null || v === undefined) v = ''
obj[h.label] = v
})
return obj
})
// 2) 生成工作表
const ws = XLSXUtils.json_to_sheet(rows, { header: headerLabels })
// 3) 列宽:根据表头和数据计算
const cols = headers.map(h => {
const headW = calcWch(h.label)
const dataW = rows.reduce((mx, r) => Math.max(mx, calcWch(r[h.label])), 0)
return { wch: Math.max(headW, dataW) }
})
ws['!cols'] = cols
// 4) 冻结首行、开启筛选
if (ws['!ref']) {
ws['!freeze'] = { xSplit: 0, ySplit: 1 }
ws['!autofilter'] = { ref: ws['!ref'] }
}
// 5) 生成工作簿并下载
const wb = XLSXUtils.book_new()
XLSXUtils.book_append_sheet(wb, ws, sheetName)
const safeName = filename.endsWith('.xlsx') ? filename : `${filename}.xlsx`
XLSXWriteFile(wb, safeName)
}
```
### 3) 用法保持不变
```js
const headers = [
{ label: '链接编号', key: 'codeNo' },
{ label: '状态', key: 'status' },
{ label: '到期时间', key: 'expiredAt' },
{ label: '备注', key: 'remark' },
]
exportToExcel(listData, headers, '链接列表.xlsx')
```
> 说明
>
> * 现在导出的是 **真正的 .xlsx 文件**Excel/Numbers/WPS 打开都不会中文乱码。
> * 不再需要 BOM、小心 Excel 旧版 HTML 方案的各种兼容问题。
> * 已对以 `= + - @` 开头的字符串做了前缀 `'` 处理,避免“公式注入”。
> * 如需多表导出,可多次 `book_append_sheet` 追加不同工作表。