148 lines
5.0 KiB
Plaintext
148 lines
5.0 KiB
Plaintext
|
|
<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>
|