elementPlus-button组件二次封装

elementPlus-button组件二次封装

目录

传递参数说明

  <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>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mebius1916

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值