Vue实战:从零到一,打造一个功能强大的“自定义导出”组件

🚀 Vue实战:从零到一,打造一个功能强大的“自定义导出”组件

嘿,各位在前端世界里创造酷炫体验的伙伴们!👋

“导出Excel”是后台管理系统中一个再常见不过的需求。但一个“优秀”的导出功能,绝不仅仅是把表格数据原样下载下来。它应该足够智能灵活,能够满足用户个性化的需求。

今天,我想带大家一起深入探索,我是如何使用 Vue.js 和 Element UI,从零到一打造一个功能强大的“自定义导出”组件。这个组件不仅能让用户自由选择和排序导出的列,还能轻松实现跨页勾选数据进行批量导出。

准备好了吗?让我们开始这场前端技术的深度之旅!

功能展示:我们最终要实现什么?

  1. 跨页选择: 用户可以在分页表格中,自由翻页,之前页面勾选的数据不会丢失。
  2. 自定义列: 点击“导出表单”后,弹出一个配置窗口。
  3. 动态配置: 在弹窗中,用户可以通过点击拖拽,决定最终导出的 Excel 文件包含哪些列,以及这些列的顺序。
  4. 记忆功能: 系统会记住用户上次的配置,下次打开时自动应用。
  5. 一键导出: 配置完成后,点击“导出”按钮,即可下载定制化的 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();
  }
}

实现解读:

  1. 加载配置 (getList): 弹窗显示时,会调用 list API (Application Programming Interface, 应用程序编程接口),从后端获取当前用户上一次保存的导出列配置,实现“记忆”功能。
  2. 动态交互 (handleTag, vuedraggable): 用户通过点击可以在“显示”和“隐藏”区域间移动字段,通过拖拽可以调整“显示”字段的顺序。这些操作都实时地更新着 this.list.displays 数组。
  3. 保存与执行 (handleSubmit): 这是最关键的一步。
    • 先保存配置: 调用 save API,将当前 this.list.displays 中的字段 ID (Identifier) 和顺序提交给后端,实现用户偏好的持久化。
    • 再触发下载: 在配置保存成功后,从 selects prop 中提取所有选中行的 ID,拼接成 URL (Uniform Resource Locator, 统一资源定位符) 参数,然后调用 getToUrl。这个 getToUrl 函数内部封装了通过请求后端接口来触发浏览器文件下载的逻辑。

结论:组合的力量

通过将 Element UI 的强大功能、Vue 的组件化和响应式思想、以及 vuedraggable 这样的优秀第三方库结合起来,我们成功地构建了一个用户体验极佳的、高度可配置的导出功能。

这个案例完美地展示了现代前端开发的魅力:通过组合不同的工具和模式,我们可以将复杂的需求,拆解成一个个清晰、可控的模块,最终优雅地实现它。

希望这次的分享能给你在构建复杂前端功能时带来一些灵感!

Happy Coding! 💻✨


总结与图表分析 📊

📝 功能实现技术栈总结表
功能点核心技术 / 库实现的关键属性/方法
跨页选择Element UI Table:row-key, :reserve-selection="true"
弹窗交互Vue.jsProps (:visible, :selects), Events (@close)
字段拖拽排序vuedraggablev-model
配置持久化axios / APIlist() (加载), save() (保存)
文件下载axios / APIgetToUrl() (触发后端文件流)
🗺️ 流程图:用户导出操作的全流程
用户跨页勾选数据
父组件: handleSelectionChange
更新 selects 数组
用户点击'导出表单'按钮
父组件: hanldeExportForm
设置 exportFormVisible = true
子组件: ExportForm 显示
子组件: getList()
调用 list API 获取历史配置
用户拖拽/点击
调整导出字段
子组件: handleSubmit()
用户点击'导出'按钮
调用 save API
保存字段配置
调用 getToUrl API
触发文件下载
浏览器下载 Excel 文件
🔄 时序图:父子组件与后端的交互
Userproduct-list.vueexport-form.vue后端API点击"导出表单"更新 prop: visible = truelist({type: 1})返回字段配置渲染字段标签调整字段点击"导出"save({idList: [...]})保存成功getToUrl(...?idList=...)返回文件流, 触发下载emit('close')更新 prop: visible = falseUserproduct-list.vueexport-form.vue后端API
🚦 状态图:导出弹窗 (exportFormVisible) 的状态
初始状态
用户点击"导出表单"
用户点击关闭或导出成功
Hidden
Shown
🏛️ 类图:Vue 组件关系
"包含并控制"
1
1
ProductList
-exportFormVisible: boolean
-selects: Array
+hanldeExportForm()
+handleExportFormClose()
+handleSelectionChange(selects)
«Component»
ExportForm
-visible: boolean
-selects: Array
+handleSubmit()
🔗 实体关系图:前后端数据交互

在这里插入图片描述

🧠 思维导图 (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(): 调用 list API (Application Programming Interface) 获取用户历史配置
        • 动态交互:
          • 显隐: @click 事件调用 handleTag() 方法,在 displayshides 数组间移动元素
          • 排序: 使用 vuedraggable 组件,v-model 绑定 list.displays 数组,实现拖拽实时排序
      • 4. 执行导出 (handleSubmit)
        • 步骤一 (保存配置):
          • 提取 list.displays 中所有字段的 id
          • 调用 save API 将最新的字段列表和顺序持久化到后端
        • 步骤二 (触发下载):
          • 提取父组件传入的 selects 中所有行的 id
          • 调用 getToUrl API,将行 ID 作为参数,请求后端文件下载接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值