Initial commit

This commit is contained in:
tanxin
2026-10-29 10:08:39 +08:00
commit 4c6243a274
124 changed files with 17359 additions and 0 deletions
@@ -0,0 +1,287 @@
<template>
<!-- 如果不显示badge,则不包裹,直接显示子内容。保证常态下的高性能。但如果设置了alwaysWrapWithContent且有slot内容,则仍然保持包裹层 -->
<slot v-if="text == '0' && alwaysWrapWithContent== false"></slot>
<!-- 如果有多个子slot,且不设badge,去掉包裹层直接露出slot,会导致flex方向错乱。此时应设置alwaysWrapWithContent为true,总是保留包裹层 -->
<!-- 微信小程序不支持外部绑定事件 -->
<template v-else>
<view v-if="hasDefaultSlot" style="overflow: visible;" class="badge-wrap" v-bind="$attrs">
<!-- 目标内容插槽 -->
<slot></slot>
<!-- badge 文字模式 -->
<text v-if="text != '' && text != '0' " :class="['text', 'position', badgeClass]" :style="badgeStyle">
{{ text }}
</text>
<!-- badge 红点模式 -->
<view v-else-if="text != '0'" :class="['dot', 'position', badgeClass]" :style="badgeStyle"></view>
</view>
<!-- 不包裹,直接显示badge 文字模式 -->
<text v-else-if="text != ''" :class="['text', badgeClass]" :style="badgeStyle">
{{ text }}
</text>
<!-- 不包裹,直接显示badge 红点模式 -->
<view v-else :class="['dot', badgeClass]" :style="badgeStyle"></view>
</template>
</template>
<script setup lang="ts">
/**
* Badge 数字角标
* @description 数字角标(徽章)组件
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.08"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* }
* }
*/
// 禁用自动属性继承,手动控制属性绑定位置
defineOptions({
inheritAttrs: false,
externalClasses: ['badge-class']
})
// 定义组件属性接口
type Props = {
/**
* @description badge 的内容。为 "0" 时不显示 badge;为空字符串时显示圆点;有文字则显示文字
* @default ""
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* }
* }
*/
text? : string,
/**
* @description badge 的内联样式,用于覆盖内置 style
* @default ""
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* }
* }
*/
badgeStyle? : string,
/**
* @description badge 的样式类,用于覆盖内置 class
* @default ""
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* }
* }
*/
badgeClass? : string.ClassString,
/**
* @description 当有 slot 内容时,即使 text 为 "0" 也保留包裹层(badge-wrap),避免多个子 slot 时 flex 方向错乱
* @default false
* @uniPlatform {
* "app": {
* "android": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.21"
* },
* "ios": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.11"
* },
* "harmony": {
* "uniVer": "x",
* "unixVer": "5.07",
* "unixVaporVer": "5.07"
* }
* },
* "web": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "mp": {
* "weixin": {
* "uniVer": "x",
* "unixVer": "5.07"
* },
* "alipay": {
* "uniVer": "x",
* "unixVer": "5.31"
* }
* }
* }
*/
alwaysWrapWithContent? : boolean
}
// 使用 withDefaults 设置默认值
const props = withDefaults(defineProps<Props>(), {
text: '',
badgeStyle: '',
badgeClass: '',
alwaysWrapWithContent: false
})
// 1. 获取插槽对象
const slots = useSlots();
// 2. 定义计算属性来判断默认插槽是否有内容
// TODO 这种判断方式的性能有待商榷,这利用了模板的隐式转换
const hasDefaultSlot = computed(() => {
// console.log('slot: ',slots['default']);
return slots['default'] != null;
});
</script>
<style>
.text {
/* background-color: rgba(225, 80, 80, 0.9); */ /* 有透明度*/
background-color: rgb(225, 80, 80); /* 无透明度*/
min-width: 14px;
height: 14px;
line-height: 14px;
border-radius: 7px;
/* padding-left: 4px; *//* 保证圆角不裁剪文字 */
/* padding-right: 4px; *//* 保证圆角不裁剪文字 */
font-size: 10px;
color: white;
text-align: center;
white-space: nowrap;
}
.dot {
background-color: rgb(225, 80, 80);
width: 8px;
height: 8px;
border-radius: 4px;
}
/* 有包裹内容,才定位在右上角。所以这个样式是一个单独的class,在slot有内容时才生效 */
.position {
position: absolute;
top: 0px; /* 定位在顶部*/
right: 0px; /* 定位在右边*/
transform: translate(50%, -50%); /* 中心向右上角偏移,保证中心在元素右上角顶点*/
}
.badge-wrap{
/* #ifdef WEB */
/* width: fit-content; */
/* #endif */
align-self: flex-start; /* 默认这样,不然width会变100%。但需要居中时得覆盖重设*/
}
</style>