267 lines
6.3 KiB
Plaintext
267 lines
6.3 KiB
Plaintext
<script lang="ts" setup>
|
|
import { ref, computed, watch, onMounted, nextTick, getCurrentInstance } from "vue"
|
|
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
|
|
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
|
|
import { xConfig } from "../../config/xConfig.uts"
|
|
|
|
type positionType = "right" | "left" | "bottomLeft" | "bottomRight" | 'top' | 'bottom'
|
|
|
|
/**
|
|
* @name 角标 xBadge
|
|
* @page /pages/index/badge
|
|
* @category 展示组件
|
|
* @description 角标
|
|
* @constant 平台兼容
|
|
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
|
*/
|
|
defineOptions({name:"xBadge"})
|
|
|
|
const proxy = getCurrentInstance()?.proxy??null;
|
|
|
|
type xBadgePropsType = {
|
|
/**
|
|
* 文字大小,可数字或者带单位
|
|
*/
|
|
fontSize: string,
|
|
/**
|
|
* 背景颜色,合法和颜色值及主题名称
|
|
*/
|
|
bgColor: string,
|
|
/**
|
|
* 文字颜色,合法和颜色值及主题名称
|
|
*/
|
|
fontColor: string,
|
|
/**
|
|
* 是否显示为点,优先级小于count,label
|
|
*/
|
|
dot: boolean,
|
|
/**
|
|
* 是否显示为文本数字,优先级小于label
|
|
*/
|
|
count: number,
|
|
/**
|
|
* 为数字时大于此值显示+号
|
|
*/
|
|
maxCount: number,
|
|
/**
|
|
* 是否显示为文本,优先级最大
|
|
*/
|
|
label: string,
|
|
/**
|
|
* 位置
|
|
*/
|
|
position: positionType,
|
|
/**
|
|
* 偏移
|
|
*/
|
|
offset: number[]
|
|
}
|
|
|
|
const props = withDefaults(defineProps<xBadgePropsType>(), {
|
|
fontSize: "9",
|
|
bgColor: "error",
|
|
fontColor: "white",
|
|
dot: true,
|
|
count: 0,
|
|
maxCount: 99,
|
|
label: "",
|
|
position: "right",
|
|
offset: ():number[] => [0, 0] as number[]
|
|
})
|
|
|
|
// 响应式数据
|
|
const padding = ref<string>('4px 4px')
|
|
const test = ref<Map<string, string>>(new Map<string, string>([['border', '2px solid red'], ['background-color', 'green']]))
|
|
|
|
// 方法
|
|
function getNodeInfo(): void {
|
|
uni.createSelectorQuery().in(proxy)
|
|
.select(".xBadge-countAndLabel")
|
|
.boundingClientRect().exec((ret) => {
|
|
if(ret.length==0) return;
|
|
let nodeinfo = ret[0] as NodeInfo
|
|
if(nodeinfo==null) return;
|
|
let width = nodeinfo.width as number
|
|
let height = nodeinfo.height as number
|
|
let max = Math.max(width, height)
|
|
let px = Math.ceil(max / 2);
|
|
// let py = max / 2;
|
|
padding.value = `${px}px ${px}px`;
|
|
})
|
|
}
|
|
|
|
// 计算属性
|
|
const _offset = computed((): number[] => {
|
|
return props.offset;
|
|
})
|
|
|
|
const _isDot = computed((): boolean => {
|
|
if (props.label != "" || props.count > 0 || !props.dot) return false;
|
|
return true;
|
|
})
|
|
|
|
const _fontColor = computed((): string => {
|
|
return getDefaultColor(props.fontColor)
|
|
})
|
|
|
|
const _fontSize = computed((): string => {
|
|
return checkIsCssUnit(props.fontSize, xConfig.unit)
|
|
})
|
|
|
|
const _label = computed((): string => {
|
|
if (props.label != "") return props.label;
|
|
if (props.count > 0 && props.count <= props.maxCount) return props.count.toString();
|
|
if (props.count <= 0) return ""
|
|
return props.maxCount.toString() + "+"
|
|
})
|
|
|
|
const _cStyles = computed((): Map<string, string>[] => {
|
|
let trs = ''
|
|
if (props.position == 'right') {
|
|
trs = 'translate(50%, -50%)'
|
|
} else if (props.position == 'left') {
|
|
trs = 'translate(-50%, -50%)'
|
|
} else if (props.position == 'bottomLeft') {
|
|
trs = 'translate(-50%, 50%)'
|
|
} else if (props.position == 'bottomRight') {
|
|
trs = 'translate(50%, 50%)'
|
|
} else if (props.position == 'top') {
|
|
trs = 'translate(0%, -50%)'
|
|
} else if (props.position == 'bottom') {
|
|
trs = 'translate(0%, 50%)'
|
|
}
|
|
let top = ''
|
|
let bottom = ''
|
|
let left = ''
|
|
let right = ''
|
|
if (props.position == 'top') {
|
|
top = '0px'
|
|
left = 'auto'
|
|
right = 'auto'
|
|
} else if (props.position == 'bottom') {
|
|
bottom = '0px'
|
|
left = 'auto'
|
|
right = 'auto'
|
|
} else if (props.position == 'right') {
|
|
top = _offset.value[1].toString() + 'px'
|
|
right = _offset.value[0].toString() + 'px'
|
|
} else if (props.position == 'left') {
|
|
top = '0px'
|
|
left = '0px'
|
|
} else if (props.position == 'bottomLeft') {
|
|
bottom = '0px'
|
|
left = '0px'
|
|
} else if (props.position == 'bottomRight') {
|
|
bottom = '0px'
|
|
right = '0px'
|
|
}
|
|
|
|
let dotMapCs = new Map<string, string>()
|
|
dotMapCs.set("background", getDefaultColor(props.bgColor))
|
|
dotMapCs.set("left", left)
|
|
dotMapCs.set("right", right)
|
|
dotMapCs.set("top", top)
|
|
dotMapCs.set("bottom", bottom)
|
|
dotMapCs.set("transform", trs)
|
|
|
|
let labelMapCs = new Map<string, string>()
|
|
|
|
labelMapCs.set("background", getDefaultColor(props.bgColor))
|
|
labelMapCs.set("left", left)
|
|
labelMapCs.set("right", right)
|
|
labelMapCs.set("top", top)
|
|
labelMapCs.set("bottom", bottom)
|
|
labelMapCs.set("transform", trs)
|
|
labelMapCs.set("visibility", _label.value == "" ? "hidden" : "visible")
|
|
|
|
nextTick(() => {
|
|
getNodeInfo();
|
|
})
|
|
|
|
return [dotMapCs, labelMapCs] as Map<string, string>[]
|
|
})
|
|
|
|
// 监听器
|
|
watch([
|
|
(): string => props.label,
|
|
(): string => props.position,
|
|
(): number => props.count,
|
|
(): number[] => props.offset,
|
|
], (): void => {
|
|
nextTick(() => {
|
|
getNodeInfo();
|
|
})
|
|
})
|
|
// 生命周期
|
|
onMounted((): void => {
|
|
getNodeInfo();
|
|
})
|
|
</script>
|
|
<template>
|
|
<view class="xBadge" :style='{padding:padding}'>
|
|
<view class="xBadgeWrap">
|
|
<text :style='_cStyles[0]!' class="xBadge-dot" :class="[_isDot?'noneShow':'nonex']"></text>
|
|
<view id="xBadge-countAndLabel" class="xBadge-countAndLabel" :class="[_isDot?'nonex':'noneShow']"
|
|
:style='_cStyles[1]!'>
|
|
<text class="xBadge-countAndLabelText"
|
|
:style='{color:_fontColor,fontSize:_fontSize}'>{{_label}}</text>
|
|
</view>
|
|
|
|
<!--
|
|
@slot 默认内容区域,你的正常内容放置在标签内
|
|
-->
|
|
<slot></slot>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<style>
|
|
.xBadge {
|
|
overflow: visible;
|
|
}
|
|
|
|
.xBadge-countAndLabel {
|
|
position: absolute;
|
|
z-index: 3;
|
|
border-radius: 100px;
|
|
/* min-width: 16px; */
|
|
padding: 0rpx 4px;
|
|
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.xBadge-countAndLabelText {
|
|
line-height: 1.5;
|
|
text-align: center;
|
|
}
|
|
|
|
.xBadge-dot {
|
|
position: absolute;
|
|
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 18px;
|
|
z-index: 3;
|
|
opacity: 0;
|
|
}
|
|
|
|
.noneShow {
|
|
opacity: 1;
|
|
}
|
|
|
|
.nonex {
|
|
opacity: 0;
|
|
}
|
|
|
|
.xBadgeWrap {
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
overflow: visible;
|
|
position: relative;
|
|
}
|
|
</style> |