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
@@ -0,0 +1,102 @@
import { ZIndex } from '../../../constants'
import { buildProps, definePropType, isNumber } from '../../../utils'
import type { ExtractPropTypes } from 'vue'
export interface BubbleBoxOptionItem {
/**
* @description 内容
*/
text: string
/**
* @description 图标
*/
icon?: string
/**
* @description 气泡弹出框文字颜色,以tn开头使用图鸟内置的颜色
*/
textColor?: string
/**
* @description 禁止点击
*/
disabled?: boolean
}
export type BubbleBoxOption = BubbleBoxOptionItem[]
export const bubbleBoxPosition = ['top', 'bottom', 'left', 'right'] as const
export const bubbleBoxProps = buildProps({
/**
* @description 气泡弹出框选项数据
*/
options: {
type: definePropType<BubbleBoxOption>(Array),
default: () => [],
},
/**
* @description 气泡弹出框位置
*/
position: {
type: String,
values: bubbleBoxPosition,
default: 'top',
},
/**
* @description 气泡弹出框宽度,默认单位 rpx
*/
width: String,
/**
* @description 气泡弹出框高度,默认单位 rpx
*/
height: String,
/**
* @description 气泡弹出框背景颜色,以tn开头使用图鸟内置的颜色
*/
bgColor: String,
/**
* @description 气泡弹出框文字颜色,以tn开头使用图鸟内置的颜色
*/
textColor: String,
/**
* @description 选项的内边距
*/
optionItemPadding: String,
/**
* @description 是否禁止点击气泡弹出框选项
*/
disabled: Boolean,
/**
* @description 点击选项后自动关闭气泡弹出框
*/
autoClose: {
type: Boolean,
default: true,
},
/**
* @description ZIndex
*/
zIndex: {
type: Number,
default: ZIndex.bubble,
},
})
export const bubbleBoxEmits = {
/**
* @description 气泡框打开事件
*/
open: () => true,
/**
* @description 气泡框关闭事件
*/
close: () => true,
/**
* @description 气泡框选项点击事件
*/
click: (index: number) => isNumber(index),
}
export type BubbleBoxProps = ExtractPropTypes<typeof bubbleBoxProps>
export type BubbleBoxEmits = typeof bubbleBoxEmits
export type BubbleBoxPosition = (typeof bubbleBoxPosition)[number]
@@ -0,0 +1,75 @@
<script lang="ts" setup>
import TnOverlay from '../../overlay/src/overlay.vue'
import TnIcon from '../../icon/src/icon.vue'
import { bubbleBoxEmits, bubbleBoxProps } from './bubble-box'
import {
useBubbleBox,
useBubbleBoxCustomStyle,
useBubbleOptions,
} from './composables'
const props = defineProps(bubbleBoxProps)
const emits = defineEmits(bubbleBoxEmits)
const {
showBubble,
openBubbleOptions,
closeBubbleOptions,
bubbleOptionClickEvent,
} = useBubbleBox(props, emits)
const {
ns,
optionsClass,
optionsStyle,
optionsAuxiliaryElementClass,
optionsAuxiliaryElementStyle,
optionItemClass,
optionItemStyle,
} = useBubbleBoxCustomStyle(props, showBubble)
const { bubbleOptions } = useBubbleOptions(props)
</script>
<template>
<view :class="[ns.b()]">
<!-- 遮罩 -->
<TnOverlay
:show="showBubble"
:opacity="0"
:z-index="zIndex - 1"
@click="closeBubbleOptions"
/>
<!-- 内容 -->
<view :class="[ns.e('content')]" @tap.stop="openBubbleOptions">
<slot />
<!-- 气泡弹框选项 -->
<view :class="[optionsClass]" :style="optionsStyle">
<!-- 小三角 -->
<view
:class="[optionsAuxiliaryElementClass]"
:style="optionsAuxiliaryElementStyle"
/>
<!-- 选项内容 -->
<scroll-view class="scroll-view" scroll-y>
<view class="options-content">
<view
v-for="(item, index) in bubbleOptions"
:key="index"
:class="[optionItemClass(item)]"
:style="optionItemStyle(item)"
@tap.stop="bubbleOptionClickEvent(item, index)"
>
<view v-if="item.icon" class="icon">
<TnIcon :name="item.icon" />
</view>
<view class="text">{{ item.text }}</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/bubble-box.scss';
</style>
@@ -0,0 +1,131 @@
import { computed, toRef } from 'vue'
import { useComponentColor, useNamespace } from '../../../../hooks'
import { formatDomSizeValue } from '../../../../utils'
import type { CSSProperties, Ref } from 'vue'
import type { BubbleBoxProps } from '../bubble-box'
import type { BubbleBoxOptionItemData } from '../types'
type OptionsClassType = (item: BubbleBoxOptionItemData) => string
type OptionsStyleType = (item: BubbleBoxOptionItemData) => CSSProperties
export const useBubbleBoxCustomStyle = (
props: BubbleBoxProps,
showBubble: Ref<boolean>
) => {
const ns = useNamespace('bubble-box')
// 解析颜色
const [bgColorClass, bgColorStyle] = useComponentColor(
toRef(props, 'bgColor'),
'bg'
)
const [borderColorClass, borderColorStyle] = useComponentColor(
toRef(props, 'bgColor'),
'border'
)
// 选项的类
const optionsClass = computed<string>(() => {
const cls: string[] = [
ns.e('options'),
ns.em('options', props.position),
ns.is('show', showBubble.value),
]
if (bgColorClass.value) cls.push(bgColorClass.value)
return cls.join(' ')
})
// 选项的样式
const optionsStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!bgColorClass.value)
style.backgroundColor = bgColorStyle.value || 'var(--tn-color-white)'
if (props.zIndex) style.zIndex = props.zIndex
if (props.width) style.width = formatDomSizeValue(props.width)
if (props.height) style.height = formatDomSizeValue(props.height)
return style
})
// 选项辅助元素的类
const optionsAuxiliaryElementClass = computed<string>(() => {
const cls: string[] = ['auxiliary-element']
if (borderColorClass.value) cls.push(borderColorClass.value)
return cls.join(' ')
})
// 选项辅助元素的样式
const optionsAuxiliaryElementStyle = computed<CSSProperties>(() => {
const style: CSSProperties = {}
if (!borderColorClass.value)
style.borderColor = borderColorStyle.value || 'var(--tn-color-white)'
// 根据不同的位置,设置不同的边框样式
if (props.position === 'top') {
style.borderRightColor = 'transparent'
style.borderBottomColor = 'transparent'
style.borderLeftColor = 'transparent'
}
if (props.position === 'right') {
style.borderTopColor = 'transparent'
style.borderBottomColor = 'transparent'
style.borderLeftColor = 'transparent'
}
if (props.position === 'bottom') {
style.borderTopColor = 'transparent'
style.borderRightColor = 'transparent'
style.borderLeftColor = 'transparent'
}
if (props.position === 'left') {
style.borderTopColor = 'transparent'
style.borderRightColor = 'transparent'
style.borderBottomColor = 'transparent'
}
return style
})
// 选项item的类
const optionItemClass = computed<OptionsClassType>(() => {
return (item: BubbleBoxOptionItemData) => {
const cls: string[] = [
ns.e('option-item'),
ns.is('disabled', item.disabled),
]
if (item.color.class) cls.push(item.color.class)
return cls.join(' ')
}
})
// 选项item的样式
const optionItemStyle = computed<OptionsStyleType>(() => {
return (item: BubbleBoxOptionItemData) => {
const style: CSSProperties = {}
if (!item.color.class)
style.color = item.color.style || 'var(--tn-text-color-primary)'
if (props.optionItemPadding) style.padding = props.optionItemPadding
return style
}
})
return {
ns,
optionsClass,
optionsStyle,
optionsAuxiliaryElementClass,
optionsAuxiliaryElementStyle,
optionItemClass,
optionItemStyle,
}
}
@@ -0,0 +1,3 @@
export * from './bubble-box-custom'
export * from './use-bubble-options'
export * from './use-bubble-box'
@@ -0,0 +1,41 @@
import { ref } from 'vue'
import type { SetupContext } from 'vue'
import type { BubbleBoxEmits, BubbleBoxProps } from '../bubble-box'
import type { BubbleBoxOptionItemData } from '../types'
export const useBubbleBox = (
props: BubbleBoxProps,
emits: SetupContext<BubbleBoxEmits>['emit']
) => {
// 显示气泡弹出框
const showBubble = ref<boolean>(false)
const openBubbleOptions = () => {
emits('open')
showBubble.value = true
}
// 关闭气泡弹出框
const closeBubbleOptions = () => {
emits('close')
showBubble.value = false
}
// 气泡框选项点击事件
const bubbleOptionClickEvent = (
item: BubbleBoxOptionItemData,
index: number
) => {
if (props.disabled || item.disabled) return
emits('click', index)
if (props.autoClose) closeBubbleOptions()
}
return {
showBubble,
openBubbleOptions,
closeBubbleOptions,
bubbleOptionClickEvent,
}
}
@@ -0,0 +1,33 @@
import { computed, ref } from 'vue'
import { useComponentColor } from '../../../../hooks'
import { isEmptyDoubleVariableInDefault } from '../../../../utils'
import type { BubbleBoxProps } from '../bubble-box'
import type { BubbleBoxOptionData } from '../types'
export const useBubbleOptions = (props: BubbleBoxProps) => {
const bubbleOptions = computed<BubbleBoxOptionData>(() => {
return props.options.map((item) => {
const textColor = ref(
isEmptyDoubleVariableInDefault(item.textColor, props.textColor)
)
const [textColorClass, textColorStyle] = useComponentColor(
textColor,
'text'
)
return {
text: item.text || '',
icon: item?.icon || '',
disabled: props.disabled || item.disabled || false,
color: {
class: textColorClass.value,
style: textColorStyle.value,
},
}
})
})
return {
bubbleOptions,
}
}
@@ -0,0 +1,3 @@
import type BubbleBox from './bubble-box.vue'
export type TnBubbleBoxInstance = InstanceType<typeof BubbleBox>
@@ -0,0 +1,13 @@
export interface BubbleBoxOptionItemData {
text: string
icon: string
disabled: boolean
color: BubbleBoxOptionDataColor
}
export interface BubbleBoxOptionDataColor {
class: string
style: string
}
export type BubbleBoxOptionData = BubbleBoxOptionItemData[]