🚀 Vue实战:从零到一,打造一个功能强大的“自定义导出”组件
嘿,各位在前端世界里创造酷炫体验的伙伴们!👋
“导出Excel”是后台管理系统中一个再常见不过的需求。但一个“优秀”的导出功能,绝不仅仅是把表格数据原样下载下来。它应该足够智能、灵活,能够满足用户个性化的需求。
今天,我想带大家一起深入探索,我是如何使用 Vue.js 和 Element UI,从零到一打造一个功能强大的“自定义导出”组件。这个组件不仅能让用户自由选择和排序导出的列,还能轻松实现跨页勾选数据进行批量导出。
准备好了吗?让我们开始这场前端技术的深度之旅!
功能展示:我们最终要实现什么?
- 跨页选择: 用户可以在分页表格中,自由翻页,之前页面勾选的数据不会丢失。
- 自定义列: 点击“导出表单”后,弹出一个配置窗口。
- 动态配置: 在弹窗中,用户可以通过点击和拖拽,决定最终导出的 Excel 文件包含哪些列,以及这些列的顺序。
- 记忆功能: 系统会记住用户上次的配置,下次打开时自动应用。
- 一键导出: 配置完成后,点击“导出”按钮,即可下载定制化的 Excel 文件。
第一站:父组件 (product-list.vue) - 跨页选择的基石
要实现导出,首先得让用户能方便地选择数据。跨页选择是第一个挑战。幸运的是,Element UI 的 el-table 提供了“开箱即用”的解决方案。
<template> 核心改造:
<el-table
:data="list"
@selection-change="handleSelectionChange"
<!-- 1. 为每一行提供唯一标识 -->
:row-key="getRowKey"
>
<!-- 2. 开启跨页选择的“记忆”功能 -->
<el-table-column type="selection" width="55" :reserve-selection="true" />
<!-- ... 其他列 ... -->
</el-table>
<script> 核心改造:
import { Component, Vue } from 'vue-property-decorator'
@Component({ ... })
export default class extends Vue {
// 用于存储所有跨页选中的行数据
public selects: any = [];
// 1. 实现 row-key 的方法
public getRowKey(row: any) {
return row.id;
}
// 2. 接收 el-table 返回的所有选中项
public handleSelectionChange(selects: any) {
this.selects = selects;
}
// ...
}
实现解读:
- :row-key=“getRowKey”: 告诉
el-table使用id作为每一行的唯一“身份证”。 - :reserve-selection=“true”: 这是魔法的核心!它命令
el-table在数据变化(如翻页)后,依然记住所有被勾选过的行的“身份证号”,并自动保持它们的选中状态。 @selection-change的行为变化: 开启reserve-selection后,selection-change事件返回的selects数组,将包含所有跨页被选中的行对象!我们只需要用this.selects变量接收它即可。
至此,跨页选择的功能就轻松实现了!
第二站:父子组件通信 - 弹出配置窗口
当用户点击“导出表单”按钮时,我们需要弹出一个配置窗口。这个窗口是一个独立的子组件 (export-form.vue)。
父组件 (product-list.vue):
<!-- 1. 触发按钮 -->
<el-button type="primary" size="mini" @click="hanldeExportForm">导出表单</el-button>
<!-- 2. 在页面中引入子组件,并通过 props 和 events 通信 -->
<div class="components-wrap">
<export-form
:selects="selects"
:visible="exportFormVisible"
@close="handleExportFormClose"
/>
</div>
// script 部分
export default class extends Vue {
// ...
public selects: any = []; // 存储跨页选择的数据
public exportFormVisible: boolean = false; // 控制弹窗的“开关”
// 打开弹窗
public hanldeExportForm() {
this.exportFormVisible = true;
}
// 关闭弹窗
public handleExportFormClose() {
this.exportFormVisible = false;
}
}
exportFormVisible: 作为一个状态“开关”,控制着子组件的显示与隐藏。- :visible=“exportFormVisible”: 通过 prop 将“开关”状态传递给子组件。
- :selects=“selects”: 通过 prop 将所有跨页选中的数据传递给子组件。
- @close=“handleExportFormClose”: 通过自定义事件,接收子组件发出的“关闭”信号。
第三站:子组件 (export-form.vue) - 自定义与导出的核心
这个子组件是整个功能的“大脑”。
<template> 核心:
<el-dialog title="导出表单" :visible="visible" @close="closeDialog">
<!-- 显示字段区域,使用 vuedraggable 实现拖拽 -->
<draggable v-model="list.displays">
<el-tag v-for="item in list.displays" @click="handleTag(1, item.id)">
{{ item.name }}
</el-tag>
</draggable>
<!-- 隐藏字段区域 -->
<el-tag v-for="item in list.hides" @click="handleTag(2, item.id)">
{{ item.name }}
</el-tag>
<div slot="footer">
<el-button @click="closeDialog">关 闭</el-button>
<el-button type="primary" @click="handleSubmit">导 出</el-button>
</div>
</el-dialog>
<script> 核心:
import { list, save } from '@/api/export-field'
import { getToUrl } from '@/api/common'
import draggable from 'vuedraggable'
export default class extends Vue {
@Prop({ default: () => [] })
public selects: any; // 接收父组件传来的选中数据
@Prop({ default: false })
public visible!: boolean; // 接收父组件传来的“开关”状态
public list: any = { displays: [], hides: [] }; // 存储字段配置
@Watch('visible')
watchVisible(v: boolean) {
if (v) this.getList(); // 弹窗一显示,就去加载历史配置
}
// 加载用户历史配置
public async getList() {
const res: any = await list({ type: 1 });
this.list = res?.data;
}
// 处理字段的显示/隐藏
public handleTag(type: number, id: number) { /* ... */ }
@Emit('close')
public closeDialog() {}
// 最终的导出操作
public async handleSubmit() {
// 1. 保存当前用户的列配置和顺序
await save({ idList: this.list.displays.map((o: any) => o.id) });
// 2. 提取所有选中行的 ID
const ids = this.selects.map(o => o.id).join(',');
// 3. 调用后端文件下载接口
getToUrl(`${this.api}product/admin/export?idList=${ids}`);
this.$message.success('操作成功');
this.closeDialog();
}
}
实现解读:
- 加载配置 (
getList): 弹窗显示时,会调用listAPI (Application Programming Interface, 应用程序编程接口),从后端获取当前用户上一次保存的导出列配置,实现“记忆”功能。 - 动态交互 (
handleTag,vuedraggable): 用户通过点击可以在“显示”和“隐藏”区域间移动字段,通过拖拽可以调整“显示”字段的顺序。这些操作都实时地更新着this.list.displays数组。 - 保存与执行 (
handleSubmit): 这是最关键的一步。- 先保存配置: 调用
saveAPI,将当前this.list.displays中的字段 ID (Identifier) 和顺序提交给后端,实现用户偏好的持久化。 - 再触发下载: 在配置保存成功后,从
selectsprop 中提取所有选中行的 ID,拼接成 URL (Uniform Resource Locator, 统一资源定位符) 参数,然后调用getToUrl。这个getToUrl函数内部封装了通过请求后端接口来触发浏览器文件下载的逻辑。
- 先保存配置: 调用
结论:组合的力量
通过将 Element UI 的强大功能、Vue 的组件化和响应式思想、以及 vuedraggable 这样的优秀第三方库结合起来,我们成功地构建了一个用户体验极佳的、高度可配置的导出功能。
这个案例完美地展示了现代前端开发的魅力:通过组合不同的工具和模式,我们可以将复杂的需求,拆解成一个个清晰、可控的模块,最终优雅地实现它。
希望这次的分享能给你在构建复杂前端功能时带来一些灵感!
Happy Coding! 💻✨
总结与图表分析 📊
📝 功能实现技术栈总结表
| 功能点 | 核心技术 / 库 | 实现的关键属性/方法 |
|---|---|---|
| 跨页选择 | Element UI Table | :row-key, :reserve-selection="true" |
| 弹窗交互 | Vue.js | Props (:visible, :selects), Events (@close) |
| 字段拖拽排序 | vuedraggable | v-model |
| 配置持久化 | axios / API | list() (加载), save() (保存) |
| 文件下载 | axios / API | getToUrl() (触发后端文件流) |
🗺️ 流程图:用户导出操作的全流程
🔄 时序图:父子组件与后端的交互
🚦 状态图:导出弹窗 (exportFormVisible) 的状态
🏛️ 类图:Vue 组件关系
🔗 实体关系图:前后端数据交互

🧠 思维导图 (Markdown Format)
- Vue实战:打造强大的“自定义导出”功能
- 🎯 核心功能
- 跨页选择数据
- 动态配置导出列 (显隐 & 排序)
- 持久化用户配置
- 一键导出
- 🛠️ 技术实现拆解
- 1. 跨页选择 (父组件:
product-list.vue)- 技术:
Element UI Table - 关键点:
:row-key: 为每行设置唯一 ID (Identifier):reserve-selection="true": 开启跨页“记忆”功能@selection-change: 事件会返回所有跨页选中的行
- 技术:
- 2. 弹窗交互 (父子组件通信)
- 父 -> 子: 使用
Props:visible: 传递布尔值,控制弹窗显示/隐藏:selects: 传递跨页选中的数据
- 子 -> 父: 使用
Events@close: 子组件通过$emit('close')通知父组件关闭
- 父 -> 子: 使用
- 3. 自定义列 (子组件:
export-form.vue)- 加载配置:
@Watch('visible'): 监听弹窗显示,触发getList()getList(): 调用listAPI (Application Programming Interface) 获取用户历史配置
- 动态交互:
- 显隐:
@click事件调用handleTag()方法,在displays和hides数组间移动元素 - 排序: 使用
vuedraggable组件,v-model绑定list.displays数组,实现拖拽实时排序
- 显隐:
- 加载配置:
- 4. 执行导出 (
handleSubmit)- 步骤一 (保存配置):
- 提取
list.displays中所有字段的id - 调用
saveAPI 将最新的字段列表和顺序持久化到后端
- 提取
- 步骤二 (触发下载):
- 提取父组件传入的
selects中所有行的id - 调用
getToUrlAPI,将行 ID 作为参数,请求后端文件下载接口
- 提取父组件传入的
- 步骤一 (保存配置):
- 1. 跨页选择 (父组件:
- 🎯 核心功能
155

被折叠的 条评论
为什么被折叠?



