elementPlus-button组件二次封装
elementPlus-button组件二次封装
目录
-
传递参数说明
-
1.time
-
2.type
-
3.icon
-
-
二次封装了什么
-
属性透传
-
预设类型
-
预设映射
-
节流约束
-
loading联动
-
-
源代码
传递参数说明
<el-button
ref="buttonRef"
v-bind="$attrs"
:type="computedProps.type"
:icon="computedProps.icon"
:disabled="isDisabled"
@click="handleClick"
>
<slot />
</el-button>
只有两个值是外部传入,其余绑定的值由内部控制
1.time
- 类型:
Number
- 默认值:1000(毫秒)
- 用途:控制按钮点击的节流时间间隔
- 使用示例:
<t-button :time="2000" @click="handleClick" /> <!-- 2秒内只能点击一次 -->
2.type
- 类型:
ButtonType
(字符串联合类型) - 可选值:
- 预设业务类型:
'add' | 'import'| 'export'| 'delete'
- 基础类型:
'default' |'primary' |'success' | 'warning' |'danger' | 'info'
- 默认值:
'default'
- 用途:设置按钮的类型和样式
- 使用示例:
<t-button type="add" /> <!-- 新增按钮:主色系 + 加号图标 --> <t-button type="import" /> <!-- 导入按钮:默认样式 + 上传图标 --> <t-button type="primary" /> <!-- 主要按钮 -->
3.icon
- 类型:String
- 默认值:‘’
- 作用:设置按钮的图标
- 说明:支持Element Plus 的所有图标名称
- 示例:icon=“Edit”
<t-button icon="search" />
二次封装了什么
属性透传
该绑定保留了elementPlus原生按钮的所有功能传参,自动透传未被声明的 props。
<el-button v-bind="$attrs" ...>
预设类型
这样我们在给组件传参的时候可以看到vscode展示的预设类型提示
在基础的类型上添加了业务类型
type ButtonType =
| 'add'
| 'import'
| 'export'
| 'delete'
| 'default'
| 'primary'
| 'success'
| 'warning'
| 'danger'
| 'info'
预设映射
将原有的if-else结构优化成map映射结构,减少代码冗余,提高开发效率
这样通过传入的业务类型就可以绑定type和icon,并且保留组件原有的icon传入设置
// 类型与图标映射
const TYPE_ICON_MAP = {
add: { type: 'primary', icon: 'CirclePlus' },
import: { type: 'default', icon: 'Upload' },
export: { type: 'default', icon: 'Download' },
delete: { type: 'danger', icon: 'Delete' }
} as const
// 根据 props.type 与 props.icon 计算出最终类型与图标
const computedProps = computed(() => {
const fallback = { type: props.type, icon: props.icon || '' }
const entry = TYPE_ICON_MAP[props.type] || fallback
const icon = innerLoading.value ? 'Loading' : (TYPE_ICON_MAP[props.type] ? entry.icon : props.icon || '')
return {
type: entry.type,
icon
}
})
节流约束
通过接收的time来进行节流限制,并且在节流触发时会激活内置的loading状态,围绕这个loading状态来展开节流,之后将点击事件暴露给父组件并移除焦点
const handleClick = (event: MouseEvent) => {
if (innerLoading.value) return
if (props.time) {
innerLoading.value = true
setTimeout(() => {
innerLoading.value = false
}, props.time)
}
emits('click', event)
buttonRef.value?.ref?.blur()
}
节流过程中会持续loading和disabled
const attrs = useAttrs()
const isDisabled = computed(() => {
return innerLoading.value || attrs.disabled
})
loading联动
这里的loading是一种状态,采用内部定义,与disabled、节流函数和loading图标联动
在节流过程中自动触发loading状态,loading状态会将button设置成disabled状态并添加loading的icon图标。
const innerLoading = ref(false)
const computedProps = computed(() => {
//省略
const icon = innerLoading.value ? 'Loading' : (TYPE_ICON_MAP[props.type] ? entry.icon : props.icon || '')
//省略
})
const buttonRef = ref<any>(null)
const handleClick = (event: MouseEvent) => {
if (innerLoading.value) return
if (props.time) {
innerLoading.value = true //loading
setTimeout(() => {
innerLoading.value = false //loading
}, props.time)
}
emits('click', event)
buttonRef.value?.ref?.blur()
}
const attrs = useAttrs()
const isDisabled = computed(() => innerLoading.value || attrs.disabled)
源代码
<template>
<el-button
ref="buttonRef"
v-bind="$attrs"
:type="computedProps.type"
:icon="computedProps.icon"
:disabled="isDisabled"
@click="handleClick"
>
<slot />
</el-button>
</template>
<script setup lang="ts" name="TButton">
import { ref, computed, useAttrs } from 'vue'
import type { PropType } from 'vue'
type ButtonType =
| 'add'
| 'import'
| 'export'
| 'delete'
| 'default'
| 'primary'
| 'success'
| 'warning'
| 'danger'
| 'info'
// 内部loading状态
const innerLoading = ref(false)
const props = defineProps({
time: {
type: Number,
default: undefined
},
type: {
type: String as PropType<ButtonType>,
default: 'default'
},
icon: {
type: String,
default: ''
}
})
// 事件
const emits = defineEmits(['click'])
// 类型与图标映射
const TYPE_ICON_MAP = {
add: { type: 'primary', icon: 'CirclePlus' },
import: { type: 'default', icon: 'Upload' },
export: { type: 'default', icon: 'Download' },
delete: { type: 'danger', icon: 'Delete' }
} as const
// 根据 props.type 与 props.icon 计算出最终类型与图标
const computedProps = computed(() => {
const fallback = { type: props.type, icon: props.icon || '' }
const entry = TYPE_ICON_MAP[props.type] || fallback
const icon = innerLoading.value ? 'Loading' : (TYPE_ICON_MAP[props.type] ? entry.icon : props.icon || '')
return {
type: entry.type,
icon
}
})
const buttonRef = ref<any>(null)
const handleClick = (event: MouseEvent) => {
if (innerLoading.value) return
if (props.time) {
innerLoading.value = true
setTimeout(() => {
innerLoading.value = false
}, props.time)
}
emits('click', event)
buttonRef.value?.ref?.blur()
}
const attrs = useAttrs()
const isDisabled = computed(() => innerLoading.value || attrs.disabled)
</script>