徽标(Badge)的实现与优化铁壁猿版(简易版)

本文基于vue3+ts+tailwindcss,从逻辑拆解到代码细节带你实现一个简易版的右上角提示徽标组件

一、需求分析与TS类型定义

在实现组件前我们先明确Badge的能力范围,并通过TS将其规范化,这个步骤是我们实现所有组件都必须要有的,根据这一步骤再像拼积木一样一点一点"搭起来"。

首先在badge组件文件夹下创建一个types.ts,导出组件的各个api来控制每一个细节

ts 复制代码
export interface BadgeProps{
    value?:number|string      //显示的值(数字或文本)
    max?:number               //最大显示值,超过显示max+
    type?:'primary'|'success'|'warning'|'danger'|'info' //其实就是默认可选集中常见颜色
    color?:string             //自定义文本颜色
    backgroundColor?:string   //自定义背景色
    isDot?:boolean            //是否为小圆点(简洁版不显示文字)
    hidden?:boolean           //是否隐藏(比如已读情况)
    showZero?:boolean         //这个主要用于处理需要显示0的情况,少见但是还是要有
}

特别说明:这里的types是一个典型的联合类型(ts),保证types只能是这五种,既限制了错误,又给予编译器完美的提示

ts 复制代码
type BadgeType='primary'|'success'|'warning'|'danger'|'info'

二、组件实现总体思路

要写好一个Badge,需要处理以下核心部分:

1.内容逻辑

  • isDot和显示数字要做出那些判断?
  • 需要实现哪些部分的计算?
  • 需要考虑哪些类型特殊判断(检查如非0、null等)?

2.显示条件

  • hidden是否隐藏
  • 内容为空时是否显示

3.样式逻辑

  • type类型切换颜色
  • 自定义颜色与默认的优先级
  • 根据内容长度决定尺寸
  • 默认位置为右上角

4.动画体验

  • 显示/隐藏使用缩放过渡

拆解完问题后,下面按模块实现

三、内容逻辑:displayValue

内容逻辑决定当前Badge应该显示什么内容。

ts 复制代码
const displayValue=computed(()=>{
    if(props.isDot) return ''   //点状不需要考虑数字逻辑
    const val=props.value       //获取值
    if(val===''||val===null||val===undefined) return ''//特殊值返回空
    if(typeof val==='number'){
        if(val<0) return '0'                           //负值(特殊值)返回0
        if(val===0&&!props.showZero) return ''         //是否显示0
        if(val>props.max) return `${props.max}+`       //超出正常值显示max+
        return val.toString()                          //正常值正常返回
    }
    return val.toString()
})

这一块逻辑清晰地处理了 Badge 最常见的展示需求。

四、显示控制:shouldShow

Badge有一些特殊场景需要隐藏,例如消息已读、需要动画时先不显示等。

ts 复制代码
const shouldShow=computed(()=>{
    if(props.hidden) return false
    if(props.isDot) return true
    return displayValue.value!==''
})

这一层的逻辑避免了空内容闪烁问题。

五、样式处理:类型+自定义颜色+动态大小

1.类型class(typeClasses)
ts 复制代码
const typeClasses=computed(()=>{
//自定义优先级高于默认
    if(props.color||props.backgroundColor){
        return ''
    }
//默认就根据types来获取对应颜色
    const types={
        primary:'bg-blue-500 text-white',
        success: 'bg-green-500 text-white',
        warning: 'bg-yellow-500 text-white',
        danger: 'bg-red-500 text-white',
        info: 'bg-gray-500 text-white',
    }
    return types[props.type]
})
2.自定义样式
ts 复制代码
const customStyle=computed(()=>{
    const style:Record<string,string>={}
    if(props.color) style.color=props.color
    if(props.backgroundColor) style.backgroundColor=props.backgroundColor
    return style
})
3.尺寸逻辑(sizeClasses)

Badge的宽应随内容长度自动变化,比如9+、99+长度或者是点。这个逻辑保证badge不会因为数字长度不好看。

ts 复制代码
const sizeClasses=computed(()=>{
    //设置属于点的小盒子宽度
    if(props.isDot){
        return 'w-2 h-2 min-w-0'
    }
    //设置字长不同的盒子自适应
    const content=displayValue.value
    if(content.length===1){
        return 'w-5 h-5 min-w-[20px]'
    }else if(content.length===2){
        return 'w-6 h-5 min-w-[24px]'
    }else{
        return 'h-5 min-w-[24px] px-1'
    }
    
})

六、Badge模板

Badge会自动叠加在插槽内容的右上角,并带有平滑缩放动画

ts 复制代码
<template>
  <div class="relative inline-block">
    <slot></slot>

    <Transition
      enter-active-class="transition-all duration-200 ease-out"
      enter-from-class="opacity-0 transform scale-0"
      enter-to-class="opacity-100 transform scale-100"
      leave-active-class="transition-all duration-200 ease-in"
      leave-from-class="opacity-100 transform scale-100"
      leave-to-class="opacity-0 transform scale-0"
    >
      <span
        v-if="shouldShow"
        :class="[
          'text-white',
          'absolute top-0 right-0 transform translate-x-1/2 -translate-y-1/2 flex items-center justify-center rounded-full text-xs font-medium leading-none z-10',
          typeClasses,
          sizeClasses,
          props.isDot ? '' : 'border-2 border-white',
        ]"
        :style="customStyle"
      >
        {{ displayValue }}
      </span>
    </Transition>
  </div>
</template>

七、细节优化与思考

1. 负数显示为 0 :避免出现 -1 条消息 这种不符合直觉的结果。

2. 超出最大值显示 max+:限制宽度,防止 UI 崩坏:

复制代码
99+  ✔
12999  ✘

3. 自定义颜色优先级更高:使 Badge 可高度定制:

ini 复制代码
<Badge backgroundColor="#ff8800" color="#fff" />

4. 小圆点模式省略所有内容检查:非常适合在线状态指示。

5. 过渡动画提升体验:从 "出现/消失" 变为 "缩放展开",更柔和自然。

八、使用实例

这样我们就完成一个简易的徽标组件,下面在外面的父组件这样使用就可以啦!

js 复制代码
<Badge :value="3">
  <button class="relative px-3 py-1 bg-gray-200 rounded">消息</button>
</Badge>

<Badge :isDot="true" type="success">
  <div class="w-8 h-8 bg-gray-300 rounded-full"></div>
</Badge>

<Badge :value="120" :max="99" type="danger"></Badge>

<Badge value="New" backgroundColor="#f60" color="#fff"></Badge>
相关推荐
gongzemin1 小时前
约课小程序增加候补功能
前端·微信小程序·小程序·云开发
王大宇_1 小时前
虚拟列表从入门到出门
前端·javascript
程序猿小蒜1 小时前
基于springboot的人口老龄化社区服务与管理平台
java·前端·spring boot·后端·spring
用户21411832636022 小时前
Google Nano Banana Pro图像生成王者归来
前端
文心快码BaiduComate2 小时前
下周感恩节!文心快码助力感恩节抽奖页快速开发
前端·后端·程序员
_小九2 小时前
【开源】耗时数月、我开发了一款功能全面的AI图床
前端·后端·图片资源
恋猫de小郭2 小时前
聊一聊 Gemini3、 AntiGravity 和 Nano Banana Pro 的体验和问题
前端·aigc·gemini
一 乐3 小时前
英语学习激励|基于java+vue的英语学习交流平台系统小程序(源码+数据库+文档)
java·前端·数据库·vue.js·学习·小程序
淡淡蓝蓝3 小时前
uni.uploadFile使用PUT方法上传图片
开发语言·前端·javascript