2025/2/8第一次更新

This commit is contained in:
爱吃咸鱼小猫咪
2025-02-08 18:50:38 +08:00
commit d7af560866
26519 changed files with 5046029 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
import { withNoopInstall } from '../../utils'
import Badge from './src/badge.vue'
export const TnBadge = withNoopInstall(Badge)
export default TnBadge
export * from './src/badge'
export type { BadgeInstance } from './src/instance'
+117
View File
@@ -0,0 +1,117 @@
import {
useComponentCustomStyleProp,
useComponentIndexProp,
} from '../../base/composables/use-component-common-props'
import { buildProps, definePropType } from '../../../utils'
import { componentTypes } from '../../../constants'
import type { ExtractPropTypes } from 'vue'
import type { ComponentIndex } from '../../base/composables/use-component-common-props'
/**
* @description 绝对定位的位置配置
*/
export interface BadgeAbsolutePositionConfig {
/**
* @description 上边距
*/
top?: string | number
/**
* @description 右边距
*/
right?: string | number
}
export const badgeProps = buildProps({
/**
* @description 徽标内容,可以是数字或者字符串,当为数字时,超过max会显示{max}+,以icon-开头则显示图标
*/
value: {
type: [String, Number],
},
/**
* @description 徽标内容最大值,在value为number时有效,超过最大值会显示{max}+
*/
max: {
type: [String, Number],
},
/**
* @description 徽标颜色类型
*/
type: {
type: String,
values: componentTypes,
default: 'primary',
},
/**
* @description 徽标背景颜色, 以tn开头则使用图鸟内置的颜色
*/
bgColor: String,
/**
* @description 徽标文字颜色, 以tn开头则使用图鸟内置的颜色
*/
textColor: String,
/**
* @description 徽标大小
*/
size: {
type: [String, Number],
},
/**
* @description 字体大小
*/
fontSize: {
type: [String, Number],
},
/**
* @description 徽标加粗
*/
bold: Boolean,
/**
* @description 自定义徽标样式
*/
customStyle: useComponentCustomStyleProp,
/**
* @description 自定义徽标类
*/
customClass: String,
/**
* @description 是否显示点徽标
*/
dot: Boolean,
/**
* @description 是否绝对定位徽标
*/
absolute: {
type: Boolean,
default: true,
},
/**
* @description 绝对定位的位置
*/
absolutePosition: {
type: definePropType<BadgeAbsolutePositionConfig>(Object),
default: () => ({}),
},
/**
* @description 绝对居中对齐
*/
absoluteCenter: {
type: Boolean,
default: true,
},
/**
* @description 点击标识
*/
index: useComponentIndexProp,
})
export const badgeEmits = {
/**
* @description 点击事件
*/
click: (index: ComponentIndex) =>
typeof index === 'number' || typeof index === 'string',
}
export type BadgeProps = ExtractPropTypes<typeof badgeProps>
export type BadgeEmits = typeof badgeEmits
+51
View File
@@ -0,0 +1,51 @@
<script lang="ts" setup>
import { computed, useSlots } from 'vue'
import TnIcon from '../../icon/src/icon.vue'
import { badgeEmits, badgeProps } from './badge'
import { useBadge, useBadgeCustomStyle } from './composables'
const props = defineProps(badgeProps)
const emits = defineEmits(badgeEmits)
const slots = useSlots()
const { ns, contentNs, badgeContentClass, badgeContentStyle } =
useBadgeCustomStyle(props)
const { showBadge, contentType, content, badgeClick } = useBadge(props, emits)
// badge所属类
const badgeClass = computed<string>(() => {
const cls: string[] = []
cls.push(ns.b())
if (!slots?.default) {
// 绝对定位
if (props.absolute) {
cls.push(ns.e('absolute'))
if (props.absoluteCenter) cls.push(ns.em('absolute', 'center'))
}
}
return cls.join(' ')
})
</script>
<template>
<view :class="[badgeClass]">
<slot />
<!-- 徽标 -->
<view
v-if="showBadge"
:class="[badgeContentClass]"
:style="badgeContentStyle"
@tap.stop="badgeClick"
>
<template v-if="content">
<TnIcon v-if="contentType === 'icon'" :name="content" />
<span v-else :class="`${contentNs.e('data')}`">{{ content }}</span>
</template>
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/badge.scss';
</style>
@@ -0,0 +1,105 @@
import { computed, toRef } from 'vue'
import {
useComponentColor,
useComponentSize,
useNamespace,
} from '../../../../hooks'
import { formatDomSizeValue, isEmpty } from '../../../../utils'
import { useBadge } from './use-badge'
import type { CSSProperties } from 'vue'
import type { BadgeProps } from '../badge'
export const useBadgeCustomStyle = (props: BadgeProps) => {
const ns = useNamespace('badge')
const contentNs = useNamespace('badge-content')
const { contentType } = useBadge(props)
// 解析背景颜色
const [bgColorClass, bgColorStyle] = useComponentColor(
toRef(props, 'bgColor'),
'bg'
)
// 解析文字颜色
const [textColorClass, textColorStyle] = useComponentColor(
toRef(props, 'textColor'),
'text'
)
// 解析尺寸大小
const { sizeType } = useComponentSize(props.size)
// 徽标内容对应的类
const badgeContentClass = computed<string>(() => {
const cls: string[] = []
cls.push(contentNs.b())
// 点徽标
if (props.dot) cls.push(contentNs.m('dot'))
// 图标徽标
if (contentType.value === 'icon') cls.push(contentNs.m('icon'))
// 绝对定位
if (props.absolute) {
cls.push(contentNs.e('absolute'))
if (props.absoluteCenter) cls.push(contentNs.em('absolute', 'center'))
}
// 设置类型颜色
if (props.type) cls.push(`tn-type-${props.type}_bg`)
// 背景颜色
if (bgColorClass.value) cls.push(bgColorClass.value)
// 字体颜色
if (textColorClass.value) cls.push(textColorClass.value)
// 尺寸大小
if (props.size && sizeType.value === 'inner')
cls.push(contentNs.m(props.size as string))
// 加粗字体
if (props.bold) cls.push('tn-text-bold')
if (props.customClass) cls.push(props.customClass)
return cls.join(' ')
})
// 徽标对应的样式
const badgeContentStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
// 背景颜色
if (bgColorStyle.value) style.backgroundColor = bgColorStyle.value
// 字体颜色
if (textColorStyle.value) style.color = textColorStyle.value
// 尺寸大小
if (
props.size &&
(sizeType.value === 'custom' || contentType.value === 'icon')
)
style.width = style.height = formatDomSizeValue(props.size)
// 字体尺寸
if (props.fontSize) style.fontSize = formatDomSizeValue(props.fontSize)
// 绝对定位是徽标偏移量
if (props.absolutePosition.top)
style.top = formatDomSizeValue(props.absolutePosition.top)
if (props.absolutePosition.right)
style.right = formatDomSizeValue(props.absolutePosition.right)
if (!isEmpty(props.customStyle)) {
Object.assign(style, props.customStyle)
}
return style
})
return {
ns,
contentNs,
badgeContentClass,
badgeContentStyle,
}
}
@@ -0,0 +1,2 @@
export * from './badge-custom'
export * from './use-badge'
@@ -0,0 +1,52 @@
import { computed } from 'vue'
import { isNumber, isString } from '../../../../utils'
import type { SetupContext } from 'vue'
import type { BadgeEmits, BadgeProps } from '../badge'
export const badgeContentTypes = ['number', 'string', 'icon'] as const
export type BadgeContentType = (typeof badgeContentTypes)[number]
export const useBadge = (
props: BadgeProps,
emits?: SetupContext<BadgeEmits>['emit']
) => {
// 判断是否需要显示角标
const showBadge = computed<boolean>(() => {
return !!props.dot || (props.value !== '' && props.value !== undefined)
})
// 显示的内容类型
const contentType = computed<BadgeContentType>(() => {
let type: BadgeContentType = 'string'
if (isNumber(props.value)) type = 'number'
if (isString(props.value) && props.value.startsWith('icon-')) type = 'icon'
return type
})
// 显示的内容
const content = computed<string>(() => {
if (props.dot) return ''
if (contentType.value === 'number' && props.max) {
const value = Number(props.value || 0)
const max = Number(props.max || 0)
return value > max ? `${max}+` : `${value}`
}
if (contentType.value === 'icon')
return (props.value as string).replace('icon-', '')
return props.value as string
})
// 角标点击事件
const badgeClick = () => {
if (emits) emits('click', props.index)
}
return {
showBadge,
contentType,
content,
badgeClick,
}
}
@@ -0,0 +1,3 @@
import type Badge from './badge.vue'
export type BadgeInstance = InstanceType<typeof Badge>