140 lines
4.3 KiB
Markdown
140 lines
4.3 KiB
Markdown
# 9.11 Excel 导入导出组件
|
||
|
||
:::tip 概述
|
||
本节介绍前端项目中的 Excel 导入导出组件,包括导入组件(ImportExcel)和导出功能(Export2Excel)的使用方法,并说明前端解析与后端导入两条链路的选择。
|
||
:::
|
||
|
||
## 导入组件(前端解析)
|
||
|
||
```vue
|
||
<template>
|
||
<n-button @click="handleImport">导入</n-button>
|
||
<ImportExcel @success="onImportSuccess" />
|
||
</template>
|
||
|
||
<script setup>
|
||
import ImportExcel from '@/components/Excel/src/ImportExcel.vue';
|
||
|
||
const onImportSuccess = (data) => {
|
||
// data 为解析后的数组,每项是对象
|
||
console.log(data);
|
||
};
|
||
</script>
|
||
```
|
||
|
||
:::tip 前端解析 vs 后端导入
|
||
- **前端解析(本组件)**:`xlsx` 库本地解析,适合导入前预览、无业务校验的场景;
|
||
- **后端导入(推荐)**:`fileUpload` 导入弹窗(内部 `n-upload` 直传 `/api/{module}/import`,自动携带 Bearer 头),由 Service 逐行 `add()`(字典反查、唯一校验、事务隔离、错误行明细),见 [7.4 数据导入导出](/zh/tools/excel.html)。
|
||
:::
|
||
|
||
### 后端导入对接
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 导入按钮 -->
|
||
<n-button @click="importVisible = true" v-perm="['sys:example:import']">导入</n-button>
|
||
|
||
<!-- 导入弹窗,templateCode 对应后端模板编码(来自项目实际的 example 模块) -->
|
||
<fileUpload
|
||
v-if="importVisible"
|
||
v-model:visible="importVisible"
|
||
api="/api/example/import"
|
||
templateCode="example_import"
|
||
dialogTitle="导入案例演示"
|
||
@success="reloadTable()"
|
||
/>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue';
|
||
import fileUpload from '@/components/importFile/index.vue';
|
||
|
||
const importVisible = ref(false);
|
||
</script>
|
||
```
|
||
|
||
`fileUpload` 组件(`ui/src/components/importFile/index.vue`)核心为 NaiveUI 上传弹窗:
|
||
|
||
```vue
|
||
<n-modal v-model:show="props.visible" preset="dialog">
|
||
<n-upload
|
||
:action="props.api"
|
||
:headers="uploadHeaders"
|
||
name="file"
|
||
:max="1"
|
||
v-model:file-list="fileList"
|
||
@before-upload="beforeUpload"
|
||
>
|
||
<n-upload-dragger>
|
||
<!-- 点击或将文件拖拽到这里上传 -->
|
||
</n-upload-dragger>
|
||
</n-upload>
|
||
<n-button quaternary type="info" @click="handleDownload">下载模板</n-button>
|
||
</n-modal>
|
||
```
|
||
|
||
导入结果由组件统一反馈(`useMessage` 提示成功条数 / 失败明细,`errors: [{ row, name, reason }]`),成功后触发 `@success` 回调刷新列表。
|
||
|
||
## 导出功能(前端导出)
|
||
|
||
```typescript
|
||
import { jsonToSheetXlsx } from '@/components/Excel/src/Export2Excel';
|
||
|
||
const handleExport = () => {
|
||
jsonToSheetXlsx({
|
||
data: tableData.value,
|
||
header: {
|
||
name: '名称',
|
||
typeText: '类型',
|
||
statusText: '状态',
|
||
createTime: '创建时间',
|
||
},
|
||
filename: '导出数据.xlsx',
|
||
});
|
||
};
|
||
```
|
||
|
||
## 后端导出对接(推荐)
|
||
|
||
```typescript
|
||
// 调用后端导出接口,返回文件 URL(全量数据 + 权限控制 + 字典翻译)
|
||
const handleExport = async () => {
|
||
const res = await exampleExport(searchParams.value); // 文件地址
|
||
window.open(res);
|
||
};
|
||
```
|
||
|
||
后端接口由 `Controller::_export()` 封装(`#[OperationLog(TYPE_EXPORT)]` + `#[Permission]` + `ExcelService`),大数据量时 Service 内改用 `chunkExport` 分批导出。
|
||
|
||
## 导入模板下载
|
||
|
||
```typescript
|
||
// 下载导入模板
|
||
const handleDownloadTemplate = async () => {
|
||
const url = await exampleExportTemplate();
|
||
window.open(url);
|
||
};
|
||
```
|
||
|
||
## 导入导出注意事项
|
||
|
||
| 场景 | 说明 |
|
||
|------|------|
|
||
| 大数据量导出 | 后端 `chunkExport` 分批导出(回调取空终止),避免内存溢出 |
|
||
| 导入模板 | 第一行为表头,第二行起为数据(行号反馈 `row = 索引 + 2`) |
|
||
| 字段映射 | 后端 `headerMap` 配置表头与字段的对应关系(未声明列忽略) |
|
||
| 枚举翻译 | 导入时 `DictCacheService::getValue` 文本转值,导出时 `serializeMaps` 值转文本 |
|
||
| 文件限制 | 仅 `xls/xlsx`、10MB 上限、finfo MIME 检测(`_import` 内置) |
|
||
|
||
## 两种方案选择
|
||
|
||
| 方式 | 适用场景 | 校验 |
|
||
|------|---------|------|
|
||
| 前端 `ImportExcel` 解析 | 导入预览、轻量转换 | 仅格式解析 |
|
||
| 后端 `/import` 接口 | 正式导入 | FormRequest/钩子/唯一性/字典全量校验 |
|
||
|
||
## 相关文档
|
||
|
||
- [数据导入导出](/zh/tools/excel.html)
|
||
- [上传配置详解](/zh/tools/upload-config.html)
|