Appearance
BizExportButton 导出按钮
导出按钮:确认 → loading(防重复点击)→ 下载 → 成功提示。三条导出通道:request(业务侧返回 Blob,组件零接口)、task-request + task-poll(服务端异步任务,进度轮询 + 直链下载)、rows(纯前端本地导出,注入数据直接生成 CSV,零后端依赖)。
基础用法
点击 → 确认 → 按钮转 loading → 自动触发浏览器下载(1s 模拟耗时)。
传入返回 Blob 的 request 与文件名,其余(确认/loading/下载/反馈)组件全部包办。
<script setup lang="ts">
/** 基础用法:确认 → loading → Blob 下载(导出实现由业务侧 request 注入,组件零接口) */
import { BizExportButton } from 'vue3-ep-biz-lib'
/** 模拟导出接口:生成 CSV Blob */
const exportCsv = async (): Promise<Blob> => {
await new Promise((resolve) => setTimeout(resolve, 1000))
const csv = '\ufeff姓名,部门,手机号\n张三,研发部,13800138000\n李四,安全管理部,13900139000'
return new Blob([csv], { type: 'text/csv;charset=utf-8' })
}
const onSuccess = (filename: string): void => {
console.info('导出完成:', filename)
}
</script>
<template>
<div>
<BizExportButton :request="exportCsv" filename="用户列表.csv" type="success" plain @success="onSuccess">
导出 CSV
</BizExportButton>
<span class="tip">点击 → 确认 → 按钮转 loading → 自动触发浏览器下载(1s 模拟耗时)。</span>
</div>
</template>
<style scoped>
.tip {
margin-left: 12px;
font-size: 13px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
其他用法
最近结果:(未导出)
:confirm="false" 免确认直接导出;失败时 error 事件与兜底提示。<script setup lang="ts">
/** 其他用法:免确认直接导出 / success+error 事件 / 失败兜底提示 */
import { ref } from 'vue'
import { BizExportButton } from 'vue3-ep-biz-lib'
const lastResult = ref('(未导出)')
const exportOk = async (): Promise<Blob> => {
await new Promise((resolve) => setTimeout(resolve, 800))
return new Blob(['正常导出内容'], { type: 'text/plain' })
}
/** 模拟导出失败(网络异常等) */
const exportFail = async (): Promise<Blob> => {
await new Promise((resolve) => setTimeout(resolve, 800))
throw new Error('模拟接口异常')
}
</script>
<template>
<div>
<div class="row">
<BizExportButton :request="exportOk" filename="免确认导出.txt" :confirm="false" @success="() => (lastResult = '成功:免确认导出.txt')">
免确认导出
</BizExportButton>
<BizExportButton :request="exportFail" filename="失败演示.txt" confirm-text="这次会失败,仍要导出吗?" @error="() => (lastResult = '失败:已收到 error 事件')">
失败演示
</BizExportButton>
</div>
<div class="tip">最近结果:{{ lastResult }}</div>
</div>
</template>
<style scoped>
.row {
display: flex;
gap: 12px;
}
.tip {
margin-top: 8px;
font-size: 13px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
列表工具栏导出
当前筛选:车间 = 一号导出成功后自动触发浏览器下载
列表页场景:按当前筛选条件导出(自定义确认文案说明范围),全量导出走
:confirm="false" 免确认直出;request 生成 csv(真实项目为服务端文件流)。<script setup lang="ts">
/** 场景:列表工具栏导出——带筛选条件确认导出 / 免确认直出 */
import { ElTag } from 'element-plus'
import { BizExportButton } from 'vue3-ep-biz-lib'
/** 按当前筛选条件生成 csv(真实项目为服务端接口返回文件流) */
const buildCsv = async (): Promise<Blob> => {
const header = '设备序列号,位置,状态,最后上报\n'
const body = Array.from({ length: 5 }, (_unused, i) => `DEV-${1000 + i},${i + 1} 号车间,${i % 2 === 0 ? '在线' : '离线'},2026-09-23 10:${i}0`)
.join('\n')
return new Blob([`\uFEFF${header}${body}`], { type: 'text/csv;charset=utf-8' })
}
</script>
<template>
<div style="display: flex; gap: 12px; align-items: center; flex-wrap: wrap; max-width: 560px">
<ElTag size="small" type="info">当前筛选:车间 = 一号</ElTag>
<BizExportButton
filename="设备台账-一号车间.csv"
:request="buildCsv"
confirm-text="将按当前筛选条件导出一号车间全部 5 台设备,是否继续?"
success-text="导出成功,文件已开始下载"
/>
<BizExportButton
filename="设备台账-全量.csv"
:request="buildCsv"
:confirm="false"
button-text="全量导出(免确认)"
type="primary"
plain
/>
<span style="font-size: 13px; color: var(--el-text-color-secondary)">导出成功后自动触发浏览器下载</span>
</div>
</template>
显示代码
大数据量异步导出
确认后按钮 loading 直到文件就绪(示例压缩为 1.5s)
大数据导出场景:服务端生成慢(示例 1.5s),按钮全程 loading,确认文案说明耗时预期。
<script setup lang="ts">
/** 场景:大数据量异步导出——服务端生成慢,按钮全程 loading */
import { ref } from 'vue'
import { BizExportButton } from 'vue3-ep-biz-lib'
const exporting = ref(false)
const slowExport = async (): Promise<Blob> => {
exporting.value = true
await new Promise((resolve) => setTimeout(resolve, 1500))
exporting.value = false
return new Blob(['\uFEFFid,name\n1,模拟大数据导出'], { type: 'text/csv;charset=utf-8' })
}
</script>
<template>
<div style="display: flex; gap: 12px; align-items: center; max-width: 520px">
<BizExportButton
filename="经营明细(近90天).csv"
:request="slowExport"
:loading="exporting"
confirm-text="数据量较大,服务端生成约需 15 秒,是否继续?"
button-text="异步导出"
type="primary"
plain
/>
<span style="font-size: 13px; color: var(--el-text-color-secondary)">确认后按钮 loading 直到文件就绪(示例压缩为 1.5s)</span>
</div>
</template>
显示代码
多格式导出
报表场景:csv(快、无格式)与 xlsx(含格式)两个导出入口并存,免确认直出。
<script setup lang="ts">
/** 场景:多格式导出按钮组——csv 快速 / xlsx 带格式 */
import { BizExportButton } from 'vue3-ep-biz-lib'
const buildCsv = async (): Promise<Blob> => {
await new Promise((resolve) => setTimeout(resolve, 300))
return new Blob(['\uFEFF日期,访问量\n09-22,14301'], { type: 'text/csv;charset=utf-8' })
}
const buildXlsx = async (): Promise<Blob> => {
await new Promise((resolve) => setTimeout(resolve, 800))
return new Blob(['xlsx 占位内容'], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
}
</script>
<template>
<div style="display: flex; gap: 12px; align-items: center; max-width: 520px">
<BizExportButton filename="流量周报.csv" :request="buildCsv" button-text="导出 CSV(快)" size="small" />
<BizExportButton filename="流量周报.xlsx" :request="buildXlsx" button-text="导出 Excel(含格式)" size="small" :confirm="false" />
</div>
</template>
显示代码
按勾选范围导出
确认文案随勾选实时变化
口径场景:包含明细 / 仅异常两个开关实时拼进确认文案——导出前把口径说清楚。
<script setup lang="ts">
/** 场景:按勾选范围导出——确认文案动态反映范围 */
import { ref } from 'vue'
import { ElCheckbox, ElTag } from 'element-plus'
import { BizExportButton } from 'vue3-ep-biz-lib'
const withDetail = ref(true)
const onlyError = ref(false)
const buildExport = async (): Promise<Blob> => {
await new Promise((resolve) => setTimeout(resolve, 400))
const scope = [withDetail.value ? '含明细' : '仅汇总', onlyError.value ? '仅异常' : '全部'].join(' + ')
return new Blob([`\uFEFF导出范围:${scope}`], { type: 'text/csv;charset=utf-8' })
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 12px; max-width: 520px">
<div style="display: flex; gap: 16px">
<ElCheckbox v-model="withDetail">包含明细行</ElCheckbox>
<ElCheckbox v-model="onlyError">仅异常记录</ElCheckbox>
</div>
<div style="display: flex; gap: 12px; align-items: center">
<BizExportButton
filename="质检报告导出.csv"
:request="buildExport"
:confirm-text="`将导出「${withDetail ? '含明细' : '仅汇总'},${onlyError ? '仅异常' : '全部记录'}」口径的数据,是否继续?`"
/>
<ElTag size="small" type="info">确认文案随勾选实时变化</ElTag>
</div>
</div>
</template>
显示代码
内置导出设置(列勾选 + 格式切换)
点开即见导出设置:列全选/单勾(不选不能开始),xlsx / csv 切换后文件名自动带扩展名。
columns + formats 配置后确认弹窗升级为「导出设置」:列全选/单勾、xlsx / csv 切换,选项随 request(options) 传给业务侧,文件名自动追加所选格式扩展名。<script setup lang="ts">
/** 场景:导出列勾选 + 格式切换——确认弹窗变导出设置(xlsx/csv),文件名自动带扩展名 */
import { BizExportButton } from 'vue3-ep-biz-lib'
import type { BizExportColumn, BizExportFormat, BizExportRequestOptions } from 'vue3-ep-biz-lib'
const columns: BizExportColumn[] = [
{ prop: 'code', label: '订单号' },
{ prop: 'customer', label: '客户' },
{ prop: 'channel', label: '渠道' },
{ prop: 'amount', label: '金额' },
{ prop: 'status', label: '状态' },
{ prop: 'remark', label: '备注' },
]
const formats: BizExportFormat[] = ['xlsx', 'csv']
/** request 收到勾选列与所选格式(真实项目按 options 调用不同接口) */
const request = (options?: BizExportRequestOptions): Promise<Blob> => {
const cols = options?.columns ?? []
const head = columns.filter((c) => cols.includes(c.prop)).map((c) => c.label)
const body = [
['SO-1001', '华东集团', '线上', '12,800', '已支付', '加急'],
['SO-1002', '西南商贸', '线下', '6,300', '待支付', '—'],
]
.filter((_r) => true)
.map((row) => row.filter((_v, i) => cols.includes(columns[i]?.prop ?? '')))
const csv = [head, ...body].map((row) => row.join(',')).join('\n')
return new Promise((resolve) => {
setTimeout(() => resolve(new Blob([`\ufeff${csv}`], { type: 'text/csv;charset=utf-8' })), 500)
})
}
</script>
<template>
<div style="display: flex; align-items: center; gap: 12px">
<BizExportButton
filename="订单列表"
:request="request"
:columns="columns"
:formats="formats"
confirm-text="按以下设置导出当前筛选结果"
>
导出订单
</BizExportButton>
<span style="font-size: 13px; color: var(--el-text-color-secondary)">
点开即见导出设置:列全选/单勾(不选不能开始),xlsx / csv 切换后文件名自动带扩展名。
</span>
</div>
</template>
显示代码
内置异步任务导出
配置列勾选 + 异步任务:建任务 → 进度条 → 成功自动触发 fileUrl 直链下载;「后台运行」不打断服务端生成。
task-request + task-poll:建任务 → 进度弹窗(percent,可「后台运行」)→ 终态 success 自动触发 fileUrl 直链下载,task-complete 携带任务 id 可登记任务中心;可与 columns/formats 组合。<script setup lang="ts">
/** 场景:异步导出任务——大数据量服务端生成文件流,轮询进度完成后直链下载 */
import { ElMessage } from 'element-plus'
import { BizExportButton } from 'vue3-ep-biz-lib'
import type { BizExportRequestOptions, BizExportTaskStatus } from 'vue3-ep-biz-lib'
let ticks = 0
const taskRequest = (options?: BizExportRequestOptions): Promise<string> =>
new Promise((resolve) => {
ElMessage.info(`创建导出任务(${options?.format ?? 'xlsx'})`)
ticks = 0
setTimeout(() => resolve(`export-${Date.now()}`), 500)
})
const taskPoll = (_taskId: string): Promise<BizExportTaskStatus> =>
new Promise((resolve) => {
ticks += 1
setTimeout(() => {
if (ticks >= 4) {
resolve({ status: 'success', percent: 100, fileUrl: '/files/sample.xlsx' })
return
}
resolve({ status: 'processing', percent: ticks * 25 })
}, 700)
})
</script>
<template>
<div style="display: flex; align-items: center; gap: 12px">
<BizExportButton
filename="全量交易流水.xlsx"
:task-request="taskRequest"
:task-poll="taskPoll"
:poll-interval="1200"
:columns="[
{ prop: 'orderNo', label: '流水号' },
{ prop: 'amount', label: '金额' },
{ prop: 'createdAt', label: '交易时间' },
]"
>
导出全量流水(约 21 万行)
</BizExportButton>
<span style="font-size: 13px; color: var(--el-text-color-secondary)">
配置列勾选 + 异步任务:建任务 → 进度条 → 成功自动触发 fileUrl 直链下载;「后台运行」不打断服务端生成。
</span>
</div>
</template>
显示代码
纯前端本地导出
注入
rows(不传 request)即走本地通道:前端直接生成 CSV 文件(\uFEFF BOM 头防 Excel 中文乱码、RFC 4180 转义逗号/引号/换行、公式注入防护——= + - @ 开头的值前置单引号避免 Excel 误执行),零后端依赖——无导出接口的旧系统、配置数据备份、当前页小数据量导出的最省方案。<script setup lang="ts">
/** 纯前端本地导出:注入 rows 即走本地通道,前端直接生成 CSV(BOM 防 Excel 乱码),零后端依赖 */
import { BizExportButton } from 'vue3-ep-biz-lib'
import type { BizExportColumn } from 'vue3-ep-biz-lib'
const columns: BizExportColumn[] = [
{ prop: 'name', label: '商品名称' },
{ prop: 'category', label: '分类' },
{ prop: 'price', label: '单价(元)' },
{ prop: 'stock', label: '库存' },
]
const rows: Array<Record<string, unknown>> = [
{ name: '挂耳咖啡礼盒', category: '饮料饮品', price: 89.0, stock: 120 },
{ name: '每日坚果 30 包', category: '休闲零食', price: 79.9, stock: 86 },
{ name: '有机生菜 500g', category: '生鲜果蔬', price: 6.5, stock: 43 },
]
</script>
<template>
<BizExportButton filename="商品清单.csv" :columns="columns" :rows="rows" confirm-text="导出当前商品清单(本地生成 CSV)?">
本地导出 CSV
</BizExportButton>
</template>
显示代码
本地导出(列勾选 + formatter)
本地导出同样支持列勾选;列
formatter 把状态码映射中文、金额千分位——导出内容与页面展示口径一致。<script setup lang="ts">
/** 本地导出列勾选 + formatter:状态码映射中文、金额千分位——导出内容与页面口径一致 */
import { BizExportButton } from 'vue3-ep-biz-lib'
import type { BizExportColumn } from 'vue3-ep-biz-lib'
const STATUS: Record<string, string> = { paid: '已支付', pending: '待支付', refund: '已退款' }
const columns: BizExportColumn[] = [
{ prop: 'orderNo', label: '订单号' },
{ prop: 'buyer', label: '买家' },
{ prop: 'status', label: '状态', formatter: (row) => STATUS[String(row.status)] ?? String(row.status) },
{
prop: 'amount',
label: '实付(元)',
formatter: (row) => Number(row.amount ?? 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 }),
},
{ prop: 'note', label: '备注' },
]
const rows: Array<Record<string, unknown>> = [
{ orderNo: 'SO-20260918-001', buyer: '李雷', status: 'paid', amount: 12980.5, note: '含发票, 周末送达' },
{ orderNo: 'SO-20260918-002', buyer: '韩梅梅', status: 'pending', amount: 899, note: '' },
{ orderNo: 'SO-20260919-003', buyer: 'Jim, Green', status: 'refund', amount: 43200, note: '含"加急"标签' },
]
</script>
<template>
<BizExportButton filename="订单导出" :columns="columns" :rows="rows" confirm-text="导出当前订单(本地生成 CSV)?">
导出订单(可选列)
</BizExportButton>
</template>
显示代码
本地导出(表格当前筛选联动)
rows 是响应式数据:与 BizTable 共享同一筛选结果,「导出当前页」即所见即所得;大数据量(10 万行级)仍建议走服务端异步任务通道。
<script setup lang="ts">
/** 表格当前页本地导出:BizTable 数据 + BizExportButton rows 注入——筛选后点导出,当前所见即所得 */
import { computed, ref } from 'vue'
import { BizExportButton, BizTable } from 'vue3-ep-biz-lib'
import type { BizExportColumn } from 'vue3-ep-biz-lib'
import { ElInput } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
type DeptRow = { id: number; name: string; dept: string; city: string; salary: number }
const all: DeptRow[] = Array.from({ length: 8 }, (_unused, i) => ({
id: i + 1,
name: `员工${i + 1}`,
dept: i % 2 === 0 ? '研发部' : '市场部',
city: ['北京', '上海', '深圳'][i % 3],
salary: 12000 + i * 850,
}))
const keyword = ref('')
/** 当前筛选结果就是要导出的数据(rows 响应式跟随筛选变化) */
const filtered = computed(() =>
all.filter((row) => keyword.value === '' || row.name.includes(keyword.value) || row.dept.includes(keyword.value)),
)
const columns = [
{ prop: 'id', label: '工号', width: 70 },
{ prop: 'name', label: '姓名' },
{ prop: 'dept', label: '部门' },
{ prop: 'city', label: '城市' },
{ prop: 'salary', label: '月薪(元)' },
]
const exportColumns: BizExportColumn[] = [
{ prop: 'id', label: '工号' },
{ prop: 'name', label: '姓名' },
{ prop: 'dept', label: '部门' },
{ prop: 'city', label: '城市' },
{ prop: 'salary', label: '月薪(元)', formatter: (row) => Number(row.salary ?? 0).toLocaleString('zh-CN') },
]
</script>
<template>
<div>
<div style="display: flex; gap: 8px; align-items: center; margin-bottom: 8px">
<ElInput v-model="keyword" placeholder="筛选姓名 / 部门" clearable :prefix-icon="Search" style="width: 220px" />
<BizExportButton
filename="员工名单.csv"
:columns="exportColumns"
:rows="filtered"
confirm-text="导出当前筛选结果(本地生成 CSV)?"
>
导出当前页
</BizExportButton>
</div>
<BizTable :columns="columns" :data="filtered" :pagination="false" border />
</div>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| filename | string | '' | 下载文件名(含扩展名;配多格式时自动追加所选格式扩展名,本地导出固定 .csv) |
| request | (options?) => Promise<Blob> | — | 同步导出实现(与 task-request / rows 三选一),resolve 二进制内容 |
| rows | Record<string, unknown>[] | — | 纯前端本地导出(与 request / task-request 三选一,request 优先):注入数据行前端直接生成 CSV(BOM 防 Excel 乱码);未配 columns 时按首行字段名推导导出列 |
| task-request | (options?) => Promise<string> | — | 异步导出:提交建任务,resolve 任务 id |
| task-poll | (taskId) => Promise<BizExportTaskStatus> | — | 异步导出:轮询任务状态直到终态 |
| poll-interval | number | 1500 | 轮询间隔(ms) |
| columns | BizExportColumn[] | — | 可导出列(配置后确认弹窗出现列勾选,默认全选);formatter 为本地导出(rows 模式)取值格式化,缺省直接取 row[prop] |
| formats | ('xlsx' | 'csv')[] | — | 可选格式(多项时确认弹窗出现格式切换) |
| confirm | boolean | true | 无 columns/formats 时是否先弹确认框 |
| confirm-text | string | '确认导出当前数据吗?' | 确认弹窗内容 |
| success-text | string | '导出成功' | 成功提示文案 |
按钮本体属性(
type/size/icon/disabled...)经v-bind="$attrs"透传 BizButton → el-button。
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| success | (filename: string) | 下载触发成功 |
| error | (reason: unknown) | request 抛错 / 任务终态 failed(组件已给失败提示) |
| task-complete | (taskId, status: BizExportTaskStatus) | 异步任务终态(含任务 id) |
Slots
| 插槽名 | 说明 |
|---|---|
| default | 按钮文本 |
Expose
| 属性/方法 | 说明 |
|---|---|
| — | 透传 BizButton 实例能力 |