Files
QXTstore/QXTfront/uni_modules/lime-badge/components/l-badge/l-badge.uvue
2025-11-05 17:34:23 +08:00

148 lines
5.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="l-badge__wrapper" v-if="$slots['default'] != null">
<slot></slot>
<text v-if="hasContent || dot" class="l-badge" ref="textRef" :class="classes" :style="[styles]">
<slot name="content">{{renderContent}}</slot>
</text>
<!-- #ifdef APP-HARMONY -->
<text v-if="hasContent || dot" class="l-badge l-badge--offscreen" ref="offscreenRef" >
<slot name="content">{{renderContent}}</slot>
</text>
<!-- #endif -->
</view>
<text v-else-if="hasContent || dot" class="l-badge" :class="classes" :style="[styles]">
<slot name="content">{{renderContent}}</slot>
</text>
</template>
<script lang="uts" setup>
/**
* Badge 徽标组件
* @description 用于展示状态标记、消息数量等提示信息,支持多种形态和定位方式
* <br> 插件类型LBadgeComponentPublicInstance
* @tutorial https://ext.dcloud.net.cn/plugin?name=lime-badge
*
* @property {string} [color] 徽标背景色支持CSS颜色值
* @property {number | string | any} content 显示内容(数字/文字)
* @property {boolean} dot 是否显示为小红点优先级高于content
* @property {number} max 数字最大值(超出显示为${max}+
* @property {Array<string | number> | any[]} offset 位置偏移量([x, y]
* @property {'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'} position 定位位置
* @property {'circle' | 'square' | 'bubble' | 'ribbon'} [shape] 形状(当前版本未实现)
* @property {boolean} showZero 数值为0时是否显示
* @property {'medium' | 'large'} [size] 尺寸(当前版本未实现)
* @property {string | number} [content] 支持字符串模板(例:'${count}条'
* @property {Array<string | number>} offset 支持单位(例:['-10rpx', '20px']
*/
import { isNumeric } from '@/uni_modules/lime-shared/isNumeric'
import { isNumber } from '@/uni_modules/lime-shared/isNumber'
import { addUnit } from '@/uni_modules/lime-shared/addUnit'
import { toBoolean } from '@/uni_modules/lime-shared/toBoolean'
import { getOffsetWithMinusString } from './utils'
import { BadgeProps } from './type'
const name = 'l-badge'
const props = withDefaults(defineProps<BadgeProps>(), {
dot: false,
max: 99,
showZero: false,
// #ifdef APP-ANDROID
// offset: [] as any[],
// #endif
// #ifndef APP-ANDROID
// offset: [] as (string | number)[],
// #endif
position: 'top-right'
})
const context = getCurrentInstance()!
const classes = computed(():Map<string, boolean>=>{
const cls = new Map<string, boolean>()
cls.set(`${name}--fixed`, toBoolean(context.slots['default']));
cls.set(`${name}--dot`, props.dot);
cls.set(`${name}--${props.position}`, context.slots['default'] != null);
return cls
})
const styles = computed(():Map<string, any|null>=>{
const style = new Map<string, any|null>()
if(toBoolean(props.color)) {
style.set('background-color', props.color!)
}
const positions = props.position.split('-');
const offset = props.offset;
if(offset != null && offset.length == 2) {
const x = offset[0];
const y = offset[1];
if(context.slots['default'] != null) {
if(positions.length == 2) {
const offsetY = positions[0], offsetX = positions[1];
if(isNumber(y)) {
const _y = y as number
style.set(offsetY, addUnit(offsetY == 'top' ? _y : -_y))
} else {
style.set(offsetY, offsetY == 'top' ? addUnit(y) : getOffsetWithMinusString(`${y}`))
}
if(isNumber(x)) {
const _x = x as number
style.set(offsetX, addUnit(offsetX == 'left' ? _x : -_x))
} else {
style.set(offsetY, offsetY == 'left' ? addUnit(x) : getOffsetWithMinusString(`${x}`))
}
}
} else {
style.set('margin-top', addUnit(y))
style.set('margin-left', addUnit(x))
}
}
return style
});
const hasContent = computed<boolean>(():boolean => {
if(toBoolean(context.slots['content'])) {
return true
}
const content = props.content;
return (content != '' && content != null && (props.showZero || content !== '0'));
});
const renderContent = computed<string>(():string=>{
const dot = props.dot
const max = props.max
const content = props.content
if(!dot && hasContent.value) {
if(max != 0 && isNumeric(content) && parseFloat(content.toString()) > max) {
return `${max}+`
}
}
if(dot) {
return ''
}
return `${content ?? ""}`
})
// #ifdef APP-HARMONY
// 鸿蒙BUG 显示不存
// 暂时先绕一下
const textRef = ref<UniTextElement|null>(null)
const offscreenRef = ref<UniTextElement|null>(null)
const resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
offscreenRef.value?.getBoundingClientRectAsync()?.then(res=>{
// const width = entries[0].target.getBoundingClientRect().width
textRef.value!.style.setProperty('width', res.width*1.05)
})
})
const stopWatch = watch(offscreenRef, (el:UniElement|null) => {
if(el== null) return
resizeObserver.observe(el)
})
onUnmounted(()=>{
stopWatch()
resizeObserver.disconnect()
})
// #endif
</script>
<style lang="scss">
@import './index-u';
</style>