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,46 @@
<script lang="ts" setup>
import { useNamespace } from '../../../hooks'
import { useColumnNoticeBar } from './composables'
const ns = useNamespace('column-notice-bar')
const { data, interval, play, vertical, noticeClickEvent } =
useColumnNoticeBar()
</script>
// #ifdef MP-WEIXIN
<script lang="ts">
export default {
options: {
// 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
virtualHost: true,
},
}
</script>
// #endif
<template>
<view :class="[ns.b()]">
<swiper
:class="[ns.e('swiper')]"
:autoplay="play"
:interval="interval"
:vertical="vertical"
circular
>
<swiper-item
v-for="(item, index) in data"
:key="index"
class="tn-text-ellipsis-1"
:class="[ns.e('swiper-item')]"
@tap.stop="noticeClickEvent(index)"
>
{{ item }}
</swiper-item>
</swiper>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/column-notice-bar.scss';
</style>
@@ -0,0 +1,4 @@
export * from './notice-bar-common-props'
export * from './use-notice-bar'
export * from './use-row-notice-bar'
export * from './use-column-notice-bar'
@@ -0,0 +1,99 @@
import { computed, toRef } from 'vue'
import { useComponentColor } from '../../../../hooks'
import { formatDomSizeValue } from '../../../../utils'
import type { CSSProperties } from 'vue'
import type { NoticeBarProps } from '../notice-bar'
type NoticeBarCommonColorClass = (
type: 'normal' | 'leftIcon' | 'rightIcon'
) => string
type NoticeBarCommonColorStyle = (
type: 'normal' | 'leftIcon' | 'rightIcon'
) => CSSProperties
export const useNoticeBarCommonProps = (props: NoticeBarProps) => {
// 解析颜色
const [bgColorClass, bgColorStyle] = useComponentColor(
toRef(props, 'bgColor'),
'bg'
)
const [textColorClass, textColorStyle] = useComponentColor(
toRef(props, 'textColor'),
'text'
)
const [leftIconColorClass, leftIconColorStyle] = useComponentColor(
toRef(props, 'leftIconColor'),
'text'
)
const [rightIconColorClass, rightIconColorStyle] = useComponentColor(
toRef(props, 'rightIconColor'),
'text'
)
// 公共类
const commonClass = computed<NoticeBarCommonColorClass>(() => {
return (type: 'normal' | 'leftIcon' | 'rightIcon' = 'normal') => {
const cls: string[] = []
if (type === 'normal') {
// 设置颜色
if (bgColorClass.value) cls.push(bgColorClass.value)
if (textColorClass.value) cls.push(textColorClass.value)
} else if (type === 'leftIcon') {
// 设置左图标颜色
if (leftIconColorClass.value) cls.push(leftIconColorClass.value)
} else if (type === 'rightIcon') {
// 设置右图标颜色
if (rightIconColorClass.value) cls.push(rightIconColorClass.value)
}
return cls.join(' ')
}
})
// 公共样式
const commonStyle = computed<NoticeBarCommonColorStyle>(() => {
return (type: 'normal' | 'leftIcon' | 'rightIcon' = 'normal') => {
const style: CSSProperties = {}
if (type === 'normal') {
// 设置颜色
if (!bgColorClass.value) {
style.backgroundColor = bgColorStyle.value || 'var(--tn-color-white)'
}
if (textColorStyle.value) {
style.color = textColorStyle.value
} else if (!bgColorClass.value && !textColorClass.value) {
style.color = 'var(--tn-text-color-primary)'
}
// 如果有设置字体大小,则设置字体大小
if (props.fontSize) style.fontSize = formatDomSizeValue(props.fontSize)
} else if (type === 'leftIcon') {
if (!leftIconColorClass.value) {
style.color =
leftIconColorStyle.value || 'var(--tn-text-color-primary)'
}
if (props.fontSize) style.fontSize = formatDomSizeValue(props.fontSize)
if (props.leftIconSize)
style.fontSize = formatDomSizeValue(props.leftIconSize)
} else if (type === 'rightIcon') {
if (!rightIconColorClass.value) {
style.color =
rightIconColorStyle.value || 'var(--tn-text-color-secondary)'
}
if (props.fontSize)
style.fontSize = `calc(${formatDomSizeValue(props.fontSize)} * 1.2)`
if (props.rightIconSize)
style.fontSize = formatDomSizeValue(props.rightIconSize)
}
return style
}
})
return {
commonClass,
commonStyle,
}
}
@@ -0,0 +1,38 @@
import { computed, inject } from 'vue'
import { noticeBarKey } from '../../../../tokens'
import { isEmptyVariableInDefault } from '../../../../utils'
export const useColumnNoticeBar = () => {
const noticeBar = inject(noticeBarKey, null)
// 通知数据
const data = computed<string[]>(() =>
isEmptyVariableInDefault(noticeBar?.data, [])
)
// 通知切换时间
const interval = computed<number>(() =>
isEmptyVariableInDefault(noticeBar?.speed, 3000)
)
// 是否播放
const play = computed<boolean>(() =>
isEmptyVariableInDefault(noticeBar?.play, true)
)
// 是否垂直轮播
const vertical = computed<boolean>(() => noticeBar?.direction === 'vertical')
// 通知点击事件
const noticeClickEvent = (index: number) => {
noticeBar?.click(index)
}
return {
data,
interval,
play,
vertical,
noticeClickEvent,
}
}
@@ -0,0 +1,47 @@
import { computed, provide, reactive, toRefs } from 'vue'
import { noticeBarKey } from '../../../../tokens'
import type { SetupContext } from 'vue'
import type { NoticeBarEmits, NoticeBarProps } from '../notice-bar'
export const useNoticeBar = (
props: NoticeBarProps,
emits: SetupContext<NoticeBarEmits>['emit']
) => {
// 显示通知栏
const showNoticeBar = computed(() => {
return props.show && !(props.autoHide && props.data.length === 0)
})
// 播放状态
const play = computed(() => !props.pause)
// 通知栏点击事件
const click = (index: number) => {
emits('click', index)
}
// 左图标点击事件
const leftIconClick = () => {
emits('left-icon-click')
}
// 右图标点击事件
const rightIconClick = () => {
emits('right-icon-click')
}
provide(
noticeBarKey,
reactive({
...toRefs(props),
play,
click,
})
)
return {
showNoticeBar,
leftIconClick,
rightIconClick,
}
}
@@ -0,0 +1,156 @@
import {
computed,
getCurrentInstance,
inject,
nextTick,
onMounted,
ref,
watch,
} from 'vue'
import { noticeBarKey } from '../../../../tokens'
import { useSelectorQuery } from '../../../../hooks'
import {
debugWarn,
generateId,
isEmptyVariableInDefault,
} from '../../../../utils'
export const useRowNoticeBar = () => {
const instance = getCurrentInstance()
const noticeBar = inject(noticeBarKey, null)
const { getSelectorNodeInfo } = useSelectorQuery(instance)
const componentId = `trnb-${generateId()}`
const componentTextId = `${componentId}-text`
// 需要显示的数据
const data = computed<string>(() => {
if (!noticeBar?.data?.length) return ''
return noticeBar.data.join(' ')
})
// 每秒显示的像素数
const speed = computed<number>(() =>
isEmptyVariableInDefault(noticeBar?.speed, 80)
)
// 动画参数
let animationDuration = 0
let animation: UniApp.Animation | null = null
const animationData = ref<any>(null)
let animationLoopTimer: ReturnType<typeof setInterval> | null = null
// 创建动画
const createAnimation = () => {
animation = uni.createAnimation({
duration: animationDuration,
timingFunction: 'linear',
})
animation
.translateX(
-(contentWidth + contentTextWidth) + Number(Math.random() * 10)
)
.step({
duration: animationDuration,
})
animation.translateX(0).step({
duration: 0,
})
animationData.value = animation.export()
}
// 创建循环动画
const createLoopAnimation = () => {
createAnimation()
animationLoopTimer = setInterval(() => {
createAnimation()
}, animationDuration + 80)
}
// 停止动画
const stopAnimation = () => {
animation = null
animationData.value = null
if (animationLoopTimer) {
clearInterval(animationLoopTimer)
animationLoopTimer = null
}
}
watch(
() => noticeBar?.play,
(newVal) => {
if (newVal) {
createLoopAnimation()
} else {
stopAnimation()
}
}
)
let initCount = 0
// 获取内容区域容器信息
let contentWidth = 0
let contentTextWidth = 0
const getContentRectInfo = async () => {
try {
const contentRectInfo = await getSelectorNodeInfo(`#${componentId}`)
const contentTextRectInfo = await getSelectorNodeInfo(
`#${componentTextId}`
)
initCount = 0
// 根据 t=s/v(时间=路程/速度)
contentWidth = contentRectInfo.width || 0
contentTextWidth = contentTextRectInfo.width || 0
animationDuration =
((contentWidth + contentTextWidth) / speed.value) * 1000
if (noticeBar?.play && noticeBar?.autoPlay) {
setTimeout(() => {
createLoopAnimation()
}, 50)
}
} catch (err) {
if (initCount > 10) {
initCount = 0
debugWarn('TnNoticeBar', `获取通知栏容器信息失败: ${err}`)
return
}
initCount++
setTimeout(() => {
getContentRectInfo()
}, 150)
}
}
// 如果修改了speed重新初始化
watch(
() => noticeBar?.speed,
() => {
stopAnimation()
getContentRectInfo()
}
)
// 通知点击事件
const noticeClickEvent = () => {
noticeBar?.click(0)
}
onMounted(() => {
nextTick(() => {
getContentRectInfo()
})
})
return {
componentId,
componentTextId,
data,
animationData,
noticeClickEvent,
}
}
@@ -0,0 +1,3 @@
import type NoticeBar from './notice-bar.vue'
export type TnNoticeBarInstance = InstanceType<typeof NoticeBar>
@@ -0,0 +1,115 @@
import { buildProps, definePropType, isNumber } from '../../../utils'
import type { ExtractPropTypes } from 'vue'
export const noticeBarScrollDirection = ['horizontal', 'vertical'] as const
export const noticeBarProps = buildProps({
/**
* @description 是否显示通知栏
*/
show: {
type: Boolean,
default: true,
},
/**
* @description 通知栏显示的数据
*/
data: {
type: definePropType<string[]>(Array),
default: () => [],
},
/**
* @description 背景颜色,以tn开头则使用图鸟内置的颜色
*/
bgColor: String,
/**
* @description 文字颜色,以tn开头则使用图鸟内置的颜色
*/
textColor: String,
/**
* @description 字体大小
*/
fontSize: String,
/**
* @description 通知栏左边显示的图标
*/
leftIcon: String,
/**
* @description 左图标颜色,以tn开头则使用图鸟内置的颜色
*/
leftIconColor: String,
/**
* @description 左图标大小,默认单位rpx
*/
leftIconSize: String,
/**
* @description 通知栏右边显示的图标
*/
rightIcon: String,
/**
* @description 右图标颜色,以tn开头则使用图鸟内置的颜色
*/
rightIconColor: String,
/**
* @description 右图标大小,默认单位rpx
*/
rightIconSize: String,
/**
* @description 通知暂停播放
*/
pause: Boolean,
/**
* @description 自动播放
*/
autoPlay: {
type: Boolean,
default: true,
},
/**
* @description 滚动方向
*/
direction: {
type: String,
values: noticeBarScrollDirection,
default: 'horizontal',
},
/**
* @description 是否采用衔接滚动,在 direction 为 horizontal 时有效
*/
loop: {
type: Boolean,
default: true,
},
/**
* @description 滚动速度,在 direction 为 horizontal 以及 loop 为 true 时表示 每秒滚动的像素数,在 direction 为 vertical 或者 direction 为 horizontal 且 loop 为 false 时表示 切换的时间间隔单位ms
*/
speed: Number,
/**
* @description 在data为空时是否自动隐藏
*/
autoHide: {
type: Boolean,
default: true,
},
})
export const noticeBarEmits = {
/**
* @description 点击通知栏
*/
click: (index: number) => isNumber(index),
/**
* @description 左图标点击事件
*/
'left-icon-click': () => true,
/**
* @description 右图标点击事件
*/
'right-icon-click': () => true,
}
export type NoticeBarProps = ExtractPropTypes<typeof noticeBarProps>
export type NoticeBarEmits = typeof noticeBarEmits
export type NoticeBarScrollDirection = (typeof noticeBarScrollDirection)[number]
@@ -0,0 +1,59 @@
<script lang="ts" setup>
import TnIcon from '../../icon/src/icon.vue'
import { useNamespace } from '../../../hooks'
import TnColumnNoticeBar from './column-notice-bar.vue'
import TnRowNoticeBar from './row-notice-bar.vue'
import { noticeBarEmits, noticeBarProps } from './notice-bar'
import { useNoticeBar, useNoticeBarCommonProps } from './composables'
const props = defineProps(noticeBarProps)
const emits = defineEmits(noticeBarEmits)
const ns = useNamespace('notice-bar')
const { showNoticeBar, leftIconClick, rightIconClick } = useNoticeBar(
props,
emits
)
const { commonClass, commonStyle } = useNoticeBarCommonProps(props)
</script>
<template>
<view
v-if="showNoticeBar"
:class="[ns.b(), commonClass('normal')]"
:style="commonStyle('normal')"
>
<!-- 左图标 -->
<view
v-if="leftIcon"
:class="[ns.e('left-icon'), commonClass('leftIcon')]"
:style="commonStyle('leftIcon')"
@tap.stop="leftIconClick"
>
<TnIcon :name="leftIcon" />
</view>
<!-- 内容 -->
<view :class="[ns.e('content')]">
<TnColumnNoticeBar
v-if="direction === 'vertical' || (direction === 'horizontal' && !loop)"
/>
<TnRowNoticeBar v-else />
</view>
<!-- 右图标 -->
<view
v-if="rightIcon"
:class="[ns.e('right-icon'), commonClass('rightIcon')]"
:style="commonStyle('rightIcon')"
@tap.stop="rightIconClick"
>
<TnIcon :name="rightIcon" />
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/notice-bar.scss';
</style>
@@ -0,0 +1,36 @@
<script lang="ts" setup>
import { useNamespace } from '../../../hooks'
import { useRowNoticeBar } from './composables'
const ns = useNamespace('row-notice-bar')
const { componentId, componentTextId, data, animationData, noticeClickEvent } =
useRowNoticeBar()
</script>
// #ifdef MP-WEIXIN
<script lang="ts">
export default {
options: {
// 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
virtualHost: true,
},
}
</script>
// #endif
<template>
<view
:id="componentId"
:class="[ns.b()]"
:animation="animationData"
@tap.stop="noticeClickEvent"
>
<view :id="componentTextId" :class="[ns.e('value')]">
{{ data }}
</view>
</view>
</template>
<style lang="scss" scoped>
@import '../../../theme-chalk/src/row-notice-bar.scss';
</style>