This commit is contained in:
oneao committed 2025-09-21 21:52:08 +08:00
1 parent d37d92996a
commit a8f3e58a27
25 files changed
+1401 -399

No files matched your search

@@ -0,0 +1,160 @@
import type { PropType, VNode } from 'vue'
import { computed, defineComponent } from 'vue'
import { getPrefixCls } from '../_utils/getPrefixCls'
export default defineComponent({
name: 'Badge',
props: {
type: {
type: String as () => 'circle' | 'dot' | 'custom' | 'ribbon',
default: 'circle',
},
count: {
type: [Number, String] as PropType<number | string>,
},
minCount: {
type: [Number, String] as PropType<number | string>,
default: undefined,
},
maxCount: {
type: [Number, String] as PropType<number | string>,
},
showZero: {
type: Boolean,
default: false,
},
ribbonText: {
type: [String, Object, Function] as PropType<string | VNode | (() => VNode)>,
default: '',
},
offset: {
type: Array as PropType<(string | number)[]>,
default: undefined,
},
color: {
type: String,
default: '',
},
backgroundColor: {
type: String,
default: '',
},
class: {
type: [String, Array, Object],
default: '',
},
style: {
type: Object,
default: () => ({}),
},
},
setup(props, { slots }) {
const prefixCls = getPrefixCls('badge')
const displayCount = computed(() => {
if (props.type !== 'circle')
return props.count
let countNum = Number(props.count)
const maxNum = props.maxCount != null ? Number(props.maxCount) : null
const minNum = props.minCount != null ? Number(props.minCount) : null
const isCountNumeric = Number.isFinite(countNum)
const isMaxNumeric = maxNum !== null && Number.isFinite(maxNum)
const isMinNumeric = minNum !== null && Number.isFinite(minNum)
// 如果传了 minCount,showZero 不生效
if (!isMinNumeric && !props.showZero && countNum === 0)
return undefined
// 先处理 minCount
if (isCountNumeric && isMinNumeric && countNum < (minNum as number)) {
countNum = minNum as number
}
// 再处理 maxCount
if (isCountNumeric && isMaxNumeric && countNum > (maxNum as number)) {
return `${maxNum}+`
}
return countNum
})
return () => {
const supStyle: Record<string, string> = {}
if (props.offset && props.offset.length === 2) {
const [x, y] = props.offset
supStyle.top = typeof y === 'number' ? `${y}px` : y
supStyle.right = typeof x === 'number' ? `${x}px` : x
}
// backgroundColor
if (props.backgroundColor) {
if (props.type === 'ribbon') {
supStyle.backgroundColor = props.backgroundColor
supStyle.color = props.backgroundColor
}
else {
supStyle.color = props.backgroundColor
}
}
const ribbonTextStyle: Record<string, string> = {}
// color
if (props.color) {
if (props.type === 'ribbon') {
ribbonTextStyle.color = props.color
}
else {
supStyle.color = props.color
}
}
let supNode = null
if (props.type === 'dot') {
supNode = <sup class={`${prefixCls}-dot`} style={supStyle}></sup>
}
else if (props.type === 'circle' && displayCount.value !== undefined) {
supNode = (
<sup class={`${prefixCls}-circle`} style={supStyle}>
{displayCount.value}
</sup>
)
}
else if (props.type === 'custom') {
supNode = (
<sup class={`${prefixCls}-custom`} style={supStyle}>
{slots.content?.()}
</sup>
)
}
else if (props.type === 'ribbon') {
const renderRibbonText = () => {
if (slots.ribbon)
return slots.ribbon()
if (typeof props.ribbonText === 'function')
return props.ribbonText()
return props.ribbonText
}
supNode = (
<sup class={`${prefixCls}-ribbon`} style={supStyle}>
<div class={`${prefixCls}-ribbon-text`} style={ribbonTextStyle}>
{renderRibbonText()}
</div>
<div class={`${prefixCls}-ribbon-corner`}></div>
</sup>
)
}
return (
<div class={[prefixCls, props.class]} style={{ ...props.style }}>
{slots.default?.()}
{supNode}
</div>
)
}
},
})