Appearance
BizButton 按钮
常用的操作按钮,基于 el-button 二次封装:原生 props / 事件 / class 全量透传,并扩展 only-icon 图标按钮模式(隐藏文本、按钮方形化,鼠标移入以 tooltip 呈现按钮文本)与图标三通道接入(组件 / 第三方字体图标 class / 插槽)。
基础用法
与 el-button 完全一致:type / size / plain / round / circle / text / link / loading / disabled / native-type 等属性与事件经 $attrs 透传,二次封装不截断原生能力。
icon 传组件(@element-plus/icons-vue);loading 时 loading 图标替换 icon,与 el-button 语义一致。<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Edit, Search } from '@element-plus/icons-vue'
import { BizButton } from 'vue3-ep-biz-lib'
const count = ref(0)
const saving = ref(false)
/** 演示 loading 与 icon 共存:loading 时 EP 渲染 loading 图标,自渲染 icon 自动让位 */
const onSave = (): void => {
saving.value = true
window.setTimeout(() => {
saving.value = false
ElMessage.success('保存成功')
}, 1500)
}
</script>
<template>
<div>
<!-- type 变体:全部经 $attrs 透传给 el-button -->
<div class="row">
<BizButton>默认按钮</BizButton>
<BizButton type="primary">主要按钮</BizButton>
<BizButton type="success">成功按钮</BizButton>
<BizButton type="warning">警告按钮</BizButton>
<BizButton type="danger">危险按钮</BizButton>
<BizButton type="info">信息按钮</BizButton>
</div>
<div class="row">
<BizButton plain>朴素按钮</BizButton>
<BizButton type="primary" round>圆角按钮</BizButton>
<!-- 组件图标通道:@element-plus/icons-vue 图标组件 -->
<BizButton type="primary" :icon="Search" @click="count++">
搜索(已点击 {{ count }} 次)
</BizButton>
<!-- loading 语义与 el-button 一致:loading 图标替换 icon -->
<BizButton type="primary" :icon="Edit" :loading="saving" @click="onSave">保存</BizButton>
<BizButton type="primary" disabled>禁用</BizButton>
</div>
</div>
</template>
<style scoped>
.row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.row + .row {
margin-top: 12px;
}
</style>
显示代码
图标按钮模式(only-icon)
only-icon 隐藏按钮文本、按钮方形化(宽高一致:默认 32px / small 24px / large 40px,取 EP 组件尺寸变量),鼠标移入显示 tooltip——文本默认自动取按钮默认插槽文案,无需重复声明;tooltip prop 可覆盖,circle / text / link 形态不受方形化干预。
disabled 状态下原生 button 不触发 hover 事件,tooltip 不显示(el-button 原生行为,与 EP 官方一致)。<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Delete, Download, Edit, View } from '@element-plus/icons-vue'
import { BizButton } from 'vue3-ep-biz-lib'
const saving = ref(false)
const onSave = (): void => {
saving.value = true
window.setTimeout(() => {
saving.value = false
ElMessage.success('已保存')
}, 1500)
}
</script>
<template>
<div>
<!-- tooltip 文本自动取按钮文本;placement / type / size 自由组合 -->
<div class="row">
<BizButton :icon="Edit" only-icon>编辑</BizButton>
<BizButton :icon="Delete" only-icon type="danger">删除</BizButton>
<BizButton :icon="View" only-icon>查看</BizButton>
<BizButton :icon="Download" only-icon type="success" tooltip-placement="bottom">下载</BizButton>
</div>
<!-- tooltip prop 覆盖 / light 主题 / 显式禁用 / 尺寸变体(方形化跟随 EP 组件尺寸变量) -->
<div class="row">
<BizButton :icon="Edit" only-icon tooltip="复制这条记录" tooltip-effect="light">编辑</BizButton>
<BizButton :icon="Edit" only-icon tooltip-disabled>编辑(无提示)</BizButton>
<BizButton :icon="Edit" only-icon size="small">编辑</BizButton>
<BizButton :icon="Edit" only-icon size="large">编辑</BizButton>
</div>
<!-- circle / text 形态不受方形化干预;loading 时 EP 渲染 loading 图标 -->
<div class="row">
<BizButton :icon="Edit" only-icon circle>编辑</BizButton>
<BizButton :icon="Edit" only-icon type="primary" text>编辑</BizButton>
<BizButton :icon="Edit" only-icon type="primary" :loading="saving" @click="onSave">保存</BizButton>
<!-- disabled 为原生行为:不触发 hover 事件,tooltip 不显示(el-button 原生限制) -->
<BizButton :icon="Delete" only-icon type="danger" disabled>删除</BizButton>
</div>
</div>
</template>
<style scoped>
.row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.row + .row {
margin-top: 12px;
}
</style>
显示代码
第三方字体图标(iconfont / RemixIcon / Font Awesome...)
icon 传 class 字符串 即接入任意字体图标库:el-button 原生 icon prop 内部按组件名解析字符串,无法承载字体 class,BizButton 对字符串自渲染 <i :class> 通道承载。字体图标 css 由业务应用自行引入(组件库不内置图标资源)。
demo 内用伪 iconfont class 模拟(离线可跑);真实项目引入 iconfont css 后原样传入
icon="iconfont icon-edit" 即可。<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { BizButton } from 'vue3-ep-biz-lib'
const onClick = (text: string): void => {
ElMessage.info(text)
}
</script>
<template>
<div>
<!--
方式一:字体 class 字符串通道。
真实项目:引入 iconfont 项目的 css(import '@/assets/iconfont/iconfont.css')
或第三方库(npm i remixicon 后 import 'remixicon/fonts/remixicon.css'),
然后把 class 原样传入:icon="iconfont icon-edit" / icon="ri-home-line"。
本 demo 用自定义伪 iconfont class 模拟(离线可跑,机制完全一致)。
-->
<div class="row">
<BizButton icon="demo-iconfont demo-icon-edit" only-icon @click="onClick('编辑')">编辑</BizButton>
<BizButton icon="demo-iconfont demo-icon-delete" only-icon type="danger">删除</BizButton>
<BizButton
icon="demo-iconfont demo-icon-download"
only-icon
type="success"
tooltip-placement="bottom"
>
下载
</BizButton>
<!-- 字体图标 + 文本混排:间距由组件按 EP 规则补齐(6/4/8px) -->
<BizButton icon="demo-iconfont demo-icon-star" type="warning" plain>收藏</BizButton>
</div>
<!--
方式二:#icon 插槽通道(iconfont symbol 模式 / 完全自定义图标结构),
优先级低于 icon prop;仅展示时 tooltip 文本取自默认插槽(或用 tooltip prop 覆盖)。
-->
<div class="row">
<BizButton only-icon tooltip="新增成员" @click="onClick('新增成员')">
新增
<template #icon>
<svg viewBox="0 0 24 24" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<path d="M11 5h2v6h6v2h-6v6h-2v-6H5v-2h6z" />
</svg>
</template>
</BizButton>
</div>
</div>
</template>
<!--
演示用伪 iconfont(离线):真实项目删除此块,替换为 iconfont / remixicon 等字体图标 css。
注意:class 作用于组件内部的 <i>,需用非 scoped 样式(biz-button 只透传 class 不做侵入)。
-->
<style>
.demo-iconfont {
font-family: inherit;
font-size: inherit;
line-height: 1;
}
.demo-iconfont.demo-icon-edit::before {
content: '\270E'; /* ✎ */
}
.demo-iconfont.demo-icon-delete::before {
content: '\2716'; /* ✖ */
}
.demo-iconfont.demo-icon-download::before {
content: '\2913'; /* ⤓ */
}
.demo-iconfont.demo-icon-star::before {
content: '\2605'; /* ★ */
}
</style>
显示代码
图标接入三通道
| 通道 | 传参形态 | 适用 |
|---|---|---|
| 组件 | :icon="Edit"(Component) | @element-plus/icons-vue 图标、业务自定义 SVG 组件 |
| 字体 class | icon="iconfont icon-edit"(string) | iconfont 字体模式、RemixIcon、Font Awesome 等一切 class 字体图标 |
| 插槽 | #icon | iconfont symbol 模式(<svg><use href="#icon-xxx"/></svg>,非 class 驱动)、完全自定义图标结构 |
ts
// 1. 业务应用全局引入字体图标 css(一次性,与组件库无关)
import '@/assets/iconfont/iconfont.css' // iconfont 项目下载到本地
import 'remixicon/fonts/remixicon.css' // 或第三方库:npm i remixicon1
2
3
2
3
vue
<!-- 2. class 原样传入即可 -->
<BizButton icon="iconfont icon-edit" only-icon>编辑</BizButton>
<BizButton icon="ri-delete-bin-line" only-icon type="danger">删除</BizButton>
<!-- symbol 模式走 #icon 插槽 -->
<BizButton only-icon>
编辑
<template #icon>
<svg class="icon" aria-hidden="true"><use xlink:href="#icon-edit" /></svg>
</template>
</BizButton>1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
表格行操作列(图标按钮组)
华东仓盘点单
9 月对账单
删除按钮点击后进入 loading(仅作演示,图标按钮同样支持) 操作列宽度敏感场景:查看 / 编辑 / 删除收敛为 3 个
only-icon 图标按钮(tooltip 补充语义),删除异步期间 loading 图标自动让位为转圈。<script setup lang="ts">
/** 场景:表格行操作列——only-icon 图标按钮组 + tooltip + 提交中 loading 让位 */
import { ref } from 'vue'
import { ElMessage, ElTooltip } from 'element-plus'
import { Delete, Edit, View } from '@element-plus/icons-vue'
import { BizButton } from 'vue3-ep-biz-lib'
const savingId = ref<number | null>(null)
const onView = (id: number): void => {
ElMessage.info(`查看 #${id}`)
}
const onEdit = (id: number): void => {
ElMessage.info(`编辑 #${id}`)
}
/** 模拟异步删除:按钮进入 loading,1 秒后恢复(loading 时图标让位为转圈) */
const onDelete = (id: number): void => {
savingId.value = id
window.setTimeout(() => {
savingId.value = null
ElMessage.success(`已删除 #${id}(示例)`)
}, 1000)
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 8px; max-width: 420px">
<div
v-for="row in [{ id: 1, name: '华东仓盘点单' }, { id: 2, name: '9 月对账单' }]"
:key="row.id"
style="display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border: 1px solid var(--el-border-color-lighter); border-radius: 6px"
>
<span>{{ row.name }}</span>
<!-- 操作列:only-icon + tooltip,宽度收敛到 3 个 24px 图标 -->
<div style="display: flex; gap: 4px">
<BizButton :icon="View" only-icon tooltip="查看详情" size="small" @click="onView(row.id)" />
<BizButton :icon="Edit" only-icon tooltip="编辑" size="small" type="primary" @click="onEdit(row.id)" />
<BizButton :icon="Delete" only-icon tooltip="删除" size="small" type="danger" :loading="savingId === row.id" @click="onDelete(row.id)" />
</div>
</div>
<ElTooltip content="loading 期间图标自动让位为加载态,无需额外配置" placement="top">
<span style="font-size: 13px; color: var(--el-text-color-secondary); align-self: flex-start">
删除按钮点击后进入 loading(仅作演示,图标按钮同样支持)
</span>
</ElTooltip>
</div>
</template>
显示代码
表单底部操作组
表单提交场景:主操作 loading、次操作 plain、放弃编辑用 link 弱化——按钮层级即操作层级。
<script setup lang="ts">
/** 场景:表单底部操作组——主操作 loading / 次操作 / 危险返回 */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { BizButton } from 'vue3-ep-biz-lib'
const saving = ref(false)
const onSave = (): void => {
saving.value = true
window.setTimeout(() => {
saving.value = false
ElMessage.success('已保存草稿')
}, 1000)
}
</script>
<template>
<div style="display: flex; gap: 12px; align-items: center; max-width: 520px; border-top: 1px solid var(--el-border-color-lighter); padding-top: 16px">
<BizButton type="primary" :loading="saving" @click="onSave">保存</BizButton>
<BizButton type="primary" plain @click="ElMessage.info('保存并进入下一步(示例)')">保存并下一步</BizButton>
<BizButton @click="ElMessage.info('已暂存')">仅暂存</BizButton>
<BizButton type="info" link @click="ElMessage.warning('未保存内容将丢弃')">放弃编辑</BizButton>
</div>
</template>
显示代码
批量操作条
已选 3 条
批量场景:已选计数 + 启用 / 停用 / 导出 / 删除四个动作——图标按钮与文字按钮混用,删除走 BizConfirmButton 二次确认。
<script setup lang="ts">
/** 场景:批量操作条——已选计数 + 图标/文字混合批量动作 */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Delete, Download, Mute, Open } from '@element-plus/icons-vue'
import { BizButton, BizConfirmButton } from 'vue3-ep-biz-lib'
const selected = ref(3)
const needSelect = (): void => {
if (selected.value === 0) ElMessage.warning('请先勾选记录')
}
</script>
<template>
<div style="display: flex; gap: 10px; align-items: center; max-width: 560px; padding: 10px 12px; background: var(--el-color-primary-light-9); border-radius: 6px">
<span style="font-size: 13px; color: var(--el-text-color-regular)">已选 <b>{{ selected }}</b> 条</span>
<BizButton type="primary" size="small" plain :icon="Open" :disabled="selected === 0" @click="needSelect">批量启用</BizButton>
<BizButton type="warning" size="small" plain :icon="Mute" :disabled="selected === 0" @click="needSelect">批量停用</BizButton>
<BizButton size="small" plain :icon="Download" :disabled="selected === 0" @click="needSelect">导出选中</BizButton>
<BizConfirmButton type="danger" size="small" plain :icon="Delete" message="删除选中的 3 条记录?此操作不可恢复。" @confirm="ElMessage.success('已批量删除')">
批量删除
</BizConfirmButton>
</div>
</template>
显示代码
按角色权限渲染
权限场景:切换角色观察按钮三态——管理员全可见、运营部分禁用(disabled + 原因提示)、访客隐藏(v-if)。
<script setup lang="ts">
/** 场景:按角色权限渲染按钮组——可见 / 禁用 / 隐藏三态 */
import { ref } from 'vue'
import { ElMessage, ElRadioGroup, ElRadioButton } from 'element-plus'
import { Delete, Edit, Stamp } from '@element-plus/icons-vue'
import { BizButton } from 'vue3-ep-biz-lib'
type Role = 'admin' | 'operator' | 'viewer'
const role = ref<Role>('admin')
const perms: Record<Role, { edit: 'visible' | 'disabled' | 'hidden'; audit: 'visible' | 'disabled' | 'hidden'; remove: 'visible' | 'disabled' | 'hidden' }> = {
admin: { edit: 'visible', audit: 'visible', remove: 'visible' },
operator: { edit: 'visible', audit: 'disabled', remove: 'disabled' },
viewer: { edit: 'hidden', audit: 'hidden', remove: 'hidden' },
}
const info = (text: string): void => {
ElMessage.info(text)
}
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 14px; max-width: 520px">
<ElRadioGroup v-model="role" size="small">
<ElRadioButton value="admin">管理员</ElRadioButton>
<ElRadioButton value="operator">运营</ElRadioButton>
<ElRadioButton value="viewer">访客</ElRadioButton>
</ElRadioGroup>
<div style="display: flex; gap: 10px">
<BizButton v-if="perms[role].edit !== 'hidden'" type="primary" :icon="Edit" :disabled="perms[role].edit === 'disabled'" @click="info('编辑')">编辑</BizButton>
<BizButton v-if="perms[role].audit !== 'hidden'" :icon="Stamp" :disabled="perms[role].audit === 'disabled'" @click="info('审核')">审核</BizButton>
<BizButton v-if="perms[role].remove !== 'hidden'" type="danger" :icon="Delete" :disabled="perms[role].remove === 'disabled'" @click="info('删除')">删除</BizButton>
<span v-if="role === 'viewer'" style="font-size: 13px; color: var(--el-text-color-secondary); align-self: center">访客无操作权限</span>
</div>
</div>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| icon | Component | string | — | 图标:组件(EP 图标 / 自定义 SVG 组件)或字体图标 class 字符串,详见图标接入三通道 |
| only-icon | boolean | false | 图标按钮模式:隐藏文本、按钮方形化,hover 显示 tooltip(文本自动取默认插槽) |
| tooltip | string | '' | tooltip 文本;留空自动取默认插槽文本;两者皆空 tooltip 自动禁用 |
| tooltip-placement | 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end' | 'top' | tooltip 弹出方位 |
| tooltip-effect | 'dark' | 'light' | 'dark' | tooltip 主题 |
| tooltip-disabled | boolean | false | 关闭 tooltip |
| tooltip-show-after | number | 0 | tooltip 显示延迟(ms) |
| tooltip-hide-after | number | 0 | tooltip 隐藏延迟(ms) |
除上述扩展属性外,其余属性全部透传至 el-button(
type/size/plain/round/circle/text/link/loading/loading-icon/disabled/native-type/autofocus/tag...)。
Events
无自定义事件,el-button 原生事件经 $attrs 原样透传:
| 事件名 | 参数 | 说明 |
|---|---|---|
| click | (event: MouseEvent) | 点击时触发(透传 el-button) |
Slots
| 插槽名 | 说明 |
|---|---|
| default | 按钮文本;only-icon 模式下作为 tooltip 文案来源(自动同步,插槽内容变化跟随) |
| icon | 自定义图标内容(iconfont symbol 模式 / 任意结构),优先级低于 icon prop |
Expose
| 属性/方法 | 说明 |
|---|---|
| buttonRef | 内部 el-button 组件实例(EP 暴露 ref:原生 button 元素) |
| focus | (): void 聚焦内部原生 button 元素 |
| blur | (): void 移除内部原生 button 元素的焦点 |