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
+12
View File
@@ -0,0 +1,12 @@
export * from './use-namespace'
export * from './use-component-color'
export * from './use-component-size'
export * from './use-prop'
export * from './use-selector-query'
export * from './use-toggle'
export * from './use-touch'
export * from './use-uniapp-system-rect-info'
export * from './use-z-index'
export * from './use-ordered-children'
export * from './use-observer'
export * from './use-long-press'
@@ -0,0 +1,54 @@
import { ref, watch } from 'vue'
import type { Ref } from 'vue'
import type { TuniaoColorName } from '../../constants'
export type ComponentColorType = TuniaoColorName | ''
export const useComponentColor = (
prop: Ref<string | undefined>,
type: ComponentColorType = ''
): [Ref<string>, Ref<string>, (val?: string) => void] => {
const classColor = ref<string>('')
const styleColor = ref<string>('')
// 匹配图标内置颜色类正则表达式
const innerColorReg = /^(tn-|gradient)/
// 匹配样式style值正在表达式
const styleColorReg =
/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{8}|[A-Fa-f0-9]{3})$|^rgb\(\d{1,3}(,\s?\d{1,3}){2}\)$|^rgba\(\d{1,3}(,\s?\d{1,3}){2},\s?0?\.?\d{1,}\)|transparent/i
// 处理传入的颜色值,判断是否为class或style
const handleColorValue = (value?: string) => {
classColor.value = ''
styleColor.value = ''
if (value === undefined) return
if (innerColorReg.test(value)) {
// 如果是背景颜色,则区分是否为渐变色
if (type === 'bg' && /.*gradient.*/.test(value)) {
// 根据__下划线分割数据
const gradientValue = value.split('__')[1]
classColor.value = `tn-gradient-bg__${gradientValue}`
return
}
classColor.value = `${value}_${type}`
}
if (styleColorReg.test(value)) {
styleColor.value = value
}
}
handleColorValue(prop.value)
watch(
() => prop.value,
(val) => {
handleColorValue(val)
}
)
// 更新颜色值和颜色类型
const updateColor = (value?: string) => {
handleColorValue(value)
}
return [classColor, styleColor, updateColor]
}
@@ -0,0 +1,17 @@
import { computed } from 'vue'
import { componentSizes } from '../../constants'
export const componentSizeTypes = ['none', 'inner', 'custom'] as const
export type ComponentSizeType = (typeof componentSizeTypes)[number]
export const useComponentSize = (size?: string | number) => {
// size类型,内置size类型还是设置的自定义size大小
const sizeType = computed<ComponentSizeType>(() => {
if (!size) return 'none'
return componentSizes.includes(size as any) ? 'inner' : 'custom'
})
return {
sizeType,
}
}
+36
View File
@@ -0,0 +1,36 @@
import type { Ref } from 'vue'
export const useLongPress = <T extends any[]>(
event: (...args: T) => void,
enabled: Ref<boolean>,
longPressIntervel = 250
) => {
// 长按判断定时器
let longPressTimer: ReturnType<typeof setTimeout> | null = null
// 清除长按判断定时器
const clearLongPressTimer = () => {
if (longPressTimer) {
clearInterval(longPressTimer)
longPressTimer = null
}
}
// 处理长按事件
const handleLongPressEvent = (...args: T) => {
if (enabled.value) {
event(...args)
clearLongPressTimer()
longPressTimer = setInterval(() => {
event(...args)
}, longPressIntervel)
} else {
event(...args)
}
}
return {
handleLongPressEvent,
clearLongPressTimer,
}
}
+114
View File
@@ -0,0 +1,114 @@
import { computed, inject, ref, unref } from 'vue'
import type { InjectionKey, Ref } from 'vue'
export const defaultNamespace = 'tn'
const _bem = (
namespace: string,
block: string,
blockSuffix: string,
element: string,
modifier: string
) => {
let cls = `${namespace}-${block}`
if (blockSuffix) {
cls += `-${blockSuffix}`
}
if (element) {
cls += `__${element}`
}
if (modifier) {
cls += `--${modifier}`
}
return cls
}
export const namespaceContextKey: InjectionKey<Ref<string | undefined>> =
Symbol('localContextKey')
export const useGetDerivedNamespace = () => {
const derivedNamespace = inject(namespaceContextKey, ref(defaultNamespace))
const namespace = computed(() => {
return unref(derivedNamespace) || defaultNamespace
})
return namespace
}
export const useNamespace = (block: string) => {
const namespace = useGetDerivedNamespace()
const b = (blockSuffix = '') =>
_bem(namespace.value, block, blockSuffix, '', '')
const e = (element?: string) =>
element ? _bem(namespace.value, block, '', element, '') : ''
const m = (modifier?: string) =>
modifier ? _bem(namespace.value, block, '', '', modifier) : ''
const be = (blockSuffix?: string, element?: string) =>
blockSuffix && element
? _bem(namespace.value, block, blockSuffix, element, '')
: ''
const em = (element?: string, modifier?: string) =>
element && modifier
? _bem(namespace.value, block, '', element, modifier)
: ''
const bm = (blockSuffix?: string, modifier?: string) =>
blockSuffix && modifier
? _bem(namespace.value, block, blockSuffix, '', modifier)
: ''
const bem = (blockSuffix?: string, element?: string, modifier?: string) =>
blockSuffix && element && modifier
? _bem(namespace.value, block, blockSuffix, element, modifier)
: ''
const is: {
(name: string, state: boolean | undefined): string
(name: string): string
} = (name: string, ...args: [boolean | undefined] | []) => {
const state = args.length >= 1 ? args[0] : true
return name && state ? `is-${name}` : ''
}
// for css var
const cssVar = (object: Record<string, string>) => {
const styles: Record<string, string> = {}
for (const key in object) {
if (object[key]) {
styles[`--${namespace.value}-${key}`] = object[key]
}
}
return styles
}
// with Block
const cssVarBlock = (object: Record<string, string>) => {
const styles: Record<string, string> = {}
for (const key in object) {
if (object[key]) {
styles[`--${namespace.value}-${block}-${key}`] = object[key]
}
}
return styles
}
const cssVarName = (name: string) => `--${namespace.value}-${name}`
const cssVarBlockName = (name: string) =>
`--${namespace.value}-${block}-${name}`
return {
namespace,
b,
e,
m,
be,
em,
bm,
bem,
is,
// css
cssVar,
cssVarName,
cssVarBlock,
cssVarBlockName,
}
}
export type UseNamespaceReturn = ReturnType<typeof useNamespace>
+65
View File
@@ -0,0 +1,65 @@
import { getCurrentInstance } from 'vue'
import { debugWarn } from '../../utils'
import type { ComponentInternalInstance } from 'vue'
export interface ObserverFnOptions {
type: 'relativeTo' | 'relativeToViewport'
selector?: string
margins: {
top?: number
right?: number
bottom?: number
left?: number
}
}
export const useObserver = (instance?: ComponentInternalInstance | null) => {
if (!instance) {
instance = getCurrentInstance()
}
// #ifdef H5 | APP-PLUS
instance = instance?.proxy?.$parent as ComponentInternalInstance | null
// #endif
if (!instance) {
debugWarn('useObserver', '请在 setup 中使用 useObserver')
}
// Observer对象
let observerInstance: UniApp.IntersectionObserver | null = null
const connectObserver = (
selector: string,
fn: (res: UniApp.ObserveResult) => void,
fnOptions: ObserverFnOptions,
options?: UniApp.CreateIntersectionObserverOptions
) => {
// 开始监听布局之前先停止旧的监听
disconnectObserver()
observerInstance = uni.createIntersectionObserver(instance, options)
if (fnOptions.type === 'relativeTo')
observerInstance.relativeTo(fnOptions?.selector || '', fnOptions.margins)
else if (fnOptions.type === 'relativeToViewport')
observerInstance.relativeToViewport(fnOptions.margins)
observerInstance.observe(selector, (res) => {
fn && fn(res)
})
}
// 停止监听布局状态
const disconnectObserver = () => {
if (observerInstance) {
observerInstance.disconnect()
observerInstance = null
}
}
return {
connectObserver,
disconnectObserver,
}
}
@@ -0,0 +1,23 @@
import { shallowRef } from 'vue'
export const useOrderedChildren = <T extends { uid: number }>() => {
const children: Record<number, T> = {}
const orderedChildren = shallowRef<T[]>([])
const addChild = (child: T) => {
children[child.uid] = child
orderedChildren.value.push(child)
}
const removeChild = (uid: number) => {
delete children[uid]
orderedChildren.value = orderedChildren.value.filter(
(child) => child.uid !== uid
)
}
return {
children: orderedChildren,
addChild,
removeChild,
}
}
+10
View File
@@ -0,0 +1,10 @@
import { computed, getCurrentInstance } from 'vue'
import { isEmptyVariableInDefault } from '../../utils'
import type { ComputedRef } from 'vue'
export const useProp = <T>(name: string): ComputedRef<T | undefined> => {
const vm = getCurrentInstance()
return computed(
() => isEmptyVariableInDefault(vm?.proxy?.$props as any)[name]
)
}
@@ -0,0 +1,75 @@
import { getCurrentInstance } from 'vue'
import { debugWarn } from '../../utils'
import type { ComponentInternalInstance } from 'vue'
export const useSelectorQuery = (
instance?: ComponentInternalInstance | null
) => {
let query: UniApp.SelectorQuery | null = null
if (!instance) {
instance = getCurrentInstance()
}
if (!instance) {
debugWarn('useSelectorQuery', 'useSelectorQuery必须在setup函数中使用')
}
// #ifndef MP-ALIPAY || APP-PLUS
query = uni.createSelectorQuery().in(instance)
// #endif
// #ifdef APP-PLUS
query = uni.createSelectorQuery().in((instance as any).ctx.$scope)
// #endif
// #ifdef MP-ALIPAY
query = uni.createSelectorQuery().in(null)
// #endif
const getSelectorNodeInfo = (selector: string): Promise<UniApp.NodeInfo> => {
return new Promise((resolve, reject) => {
if (query) {
query
.select(selector)
.boundingClientRect((res) => {
const selectRes: UniApp.NodeInfo = res as UniApp.NodeInfo
if (selectRes) {
resolve(selectRes)
} else {
reject(new Error(`未找到对应节点: ${selector}`))
}
})
.exec()
} else {
reject(new Error('未找到对应的SelectorQuery实例'))
}
})
}
const getSelectorNodeInfos = (
selector: string
): Promise<UniApp.NodeInfo[]> => {
return new Promise((resolve, reject) => {
if (query) {
query
.selectAll(selector)
.boundingClientRect((res) => {
const selectRes: UniApp.NodeInfo[] = res as UniApp.NodeInfo[]
if (selectRes && selectRes.length > 0) {
resolve(selectRes)
} else {
reject(new Error(`未找到对应节点: ${selector}`))
}
})
.exec()
} else {
reject(new Error('未找到对应的SelectorQuery实例'))
}
})
}
return {
query,
getSelectorNodeInfo,
getSelectorNodeInfos,
}
}
+11
View File
@@ -0,0 +1,11 @@
import { ref } from 'vue'
import type { Ref } from 'vue'
export const useToggle = (initState: boolean): [Ref<boolean>, () => void] => {
const state = ref<boolean>(initState)
const toggle = () => {
state.value = !state.value
}
return [state, toggle]
}
+147
View File
@@ -0,0 +1,147 @@
import { ref } from 'vue'
export interface TouchOptions {
/**
* @description 是否禁用
*/
disabled: boolean
/**
* @description 容器节点的左边界坐标
*/
left: number
/**
* @description 容器节点的右边界坐标
*/
right: number
/**
* @description 容器节点的上边界坐标
*/
top: number
/**
* @description 容器节点的下边界坐标
*/
bottom: number
/**
* @description 方向判断容错值
*/
faultTolerance: number
}
export const useTouch = () => {
// 触摸事件配置
const options: TouchOptions = {
disabled: false,
left: 0,
right: 0,
top: 0,
bottom: 0,
faultTolerance: 10,
}
// 开始坐标,减去开始坐标
const startX = ref(0)
const startY = ref(0)
// 当前坐标,减去开始坐标
const currentX = ref(0)
const currentY = ref(0)
// 移动偏移量
const deltaX = ref(0)
const deltaY = ref(0)
// 移动距离
const distanceX = ref(0)
const distanceY = ref(0)
// 移动方向
const isVertical = ref(false)
const isHorizontal = ref(false)
// 是否为点击
const isClick = ref(false)
// 标记开始触摸
let touchFlag: 'touch' | 'moving' | 'end'
// 更新配置
const updateOptions = (newOptions: Partial<TouchOptions>) => {
Object.assign(options, newOptions)
}
// 开始触摸事件
const onTouchStart = (event: TouchEvent) => {
if (options.disabled || !event.changedTouches[0]) return
startX.value = _edgeProcessing(event.changedTouches[0].pageX, 'x')
startY.value = _edgeProcessing(event.changedTouches[0].pageY, 'y')
touchFlag = 'touch'
}
// 开始滑动事件
const onTouchMove = (event: TouchEvent) => {
if (options.disabled || !event.changedTouches[0]) return
currentX.value = _edgeProcessing(event.changedTouches[0].pageX, 'x')
currentY.value = _edgeProcessing(event.changedTouches[0].pageY, 'y')
updateDistanceInfo()
touchFlag = 'moving'
}
// 触摸结束事件
const onTouchEnd = (event: TouchEvent) => {
if (options.disabled || !event.changedTouches[0] || touchFlag === 'end')
return
currentX.value = _edgeProcessing(event.changedTouches[0].pageX, 'x')
currentY.value = _edgeProcessing(event.changedTouches[0].pageY, 'y')
updateDistanceInfo()
isVertical.value =
distanceX.value < options.faultTolerance &&
distanceY.value >= options.faultTolerance
isHorizontal.value =
distanceX.value >= options.faultTolerance &&
distanceY.value < options.faultTolerance
isClick.value = !isHorizontal.value && !isVertical.value
touchFlag = 'end'
}
// 更新距离信息
const updateDistanceInfo = () => {
deltaX.value = currentX.value - startX.value
deltaY.value = currentY.value - startY.value
distanceX.value = Math.abs(deltaX.value)
distanceY.value = Math.abs(deltaY.value)
}
// 边缘位置处理
const _edgeProcessing = (
touchPosition: number,
direction: 'x' | 'y'
): number => {
const { left, right, top, bottom } = options
if (direction === 'x') {
if (touchPosition < left) return 0
if (touchPosition > right) return right - left
return touchPosition - left
} else {
if (touchPosition < top) return 0
if (touchPosition > bottom) return bottom - top
return touchPosition - top
}
}
return {
startX,
startY,
currentX,
currentY,
deltaX,
deltaY,
distanceX,
distanceY,
isVertical,
isHorizontal,
isClick,
updateOptions,
onTouchStart,
onTouchMove,
onTouchEnd,
}
}
@@ -0,0 +1,138 @@
import { reactive } from 'vue'
import { debugWarn } from '../../utils'
export interface NavBarInfo {
height: number
statusHeight: number
}
export interface NavbarBoundingInfo {
width: number
height: number
top: number
right: number
bottom: number
left: number
marginRight: number
}
export interface SystemScreenInfo {
width: number
height: number
operationHeight: number
}
// 默认状态栏高度
const DEFAULT_STATUS_BAR_HEIGHT = 45
// 默认胶囊的宽度
const DEFAULT_NAVBAR_BOUNDING_WIDTH = 87
// 默认胶囊的高度
const DEFAULT_NAVBAR_BOUNDING_HEIGHT = 32
// 默认胶囊的右边距
const DEFAULT_NAVBAR_BOUNDING_RIGHT = 7
// 默认胶囊的上边距
const DEFAULT_NAVBAR_BOUNDING_TOP = 4
export const useUniAppSystemRectInfo = () => {
// 状态栏信息
const navBarInfo = reactive<NavBarInfo>({
height: 0,
statusHeight: DEFAULT_STATUS_BAR_HEIGHT,
})
// 状态栏胶囊信息
const navBarBoundingInfo = reactive<NavbarBoundingInfo>({
width: 0,
height: 32,
top: 0,
right: 0,
bottom: 0,
left: 0,
marginRight: 0,
})
// 系统屏幕信息
const systemScreenInfo = reactive<SystemScreenInfo>({
width: 0,
height: 0,
operationHeight: 0,
})
const getSystemRectInfo = () => {
try {
const uniSystemInfo = uni.getSystemInfoSync()
const { statusBarHeight, windowWidth, windowHeight, titleBarHeight } =
uniSystemInfo
let height = 0
// #ifndef MP
height = (statusBarHeight || 0) + DEFAULT_STATUS_BAR_HEIGHT
navBarBoundingInfo.width = DEFAULT_NAVBAR_BOUNDING_WIDTH
navBarBoundingInfo.height = DEFAULT_NAVBAR_BOUNDING_HEIGHT
navBarBoundingInfo.right = windowWidth - DEFAULT_NAVBAR_BOUNDING_RIGHT
navBarBoundingInfo.left =
windowWidth -
DEFAULT_NAVBAR_BOUNDING_RIGHT -
DEFAULT_NAVBAR_BOUNDING_WIDTH
navBarBoundingInfo.top = DEFAULT_NAVBAR_BOUNDING_TOP
navBarBoundingInfo.bottom =
DEFAULT_NAVBAR_BOUNDING_TOP + DEFAULT_NAVBAR_BOUNDING_HEIGHT
navBarBoundingInfo.marginRight = DEFAULT_NAVBAR_BOUNDING_RIGHT
// #endif
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP_TOUTIAO || MP-QQ
const {
width: menuButtonWidth,
height: menuButtonHeight,
bottom: menuButtonBottom,
top: menuButtonTop,
left: menuButtonLeft,
right: menuButtonRight,
} = uni.getMenuButtonBoundingClientRect()
navBarBoundingInfo.width = menuButtonWidth
navBarBoundingInfo.height = menuButtonHeight + 2
navBarBoundingInfo.bottom = menuButtonBottom
navBarBoundingInfo.top = menuButtonTop
navBarBoundingInfo.left = menuButtonLeft
navBarBoundingInfo.right = menuButtonRight
// #ifdef MP-ALIPAY
navBarBoundingInfo.right = menuButtonLeft + menuButtonWidth
// #endif
navBarBoundingInfo.marginRight = windowWidth - navBarBoundingInfo.right
// 防止导航栏内容区域太靠近底部
// 菜单胶囊按钮距离顶部的高度
const menuButtonMarginTopHeight = menuButtonTop - statusBarHeight!
height =
menuButtonBottom +
(menuButtonMarginTopHeight < 4 ? 4 : menuButtonMarginTopHeight)
// #endif
// #ifdef MP-ALIPAY
const { optionMenuLeft } = uni.getMenuButtonBoundingClientRect()
height = statusBarHeight! + titleBarHeight!
if (optionMenuLeft) {
navBarBoundingInfo.left = optionMenuLeft
}
// #endif
navBarInfo.height = height
navBarInfo.statusHeight = statusBarHeight!
systemScreenInfo.width = windowWidth
systemScreenInfo.height = windowHeight
systemScreenInfo.operationHeight = windowHeight - height
} catch (err) {
debugWarn(
'useUniAppSystemRectInfo',
`[TnGetSystemRectInfo]获取系统容器信息失败: ${err}`
)
}
}
getSystemRectInfo()
return {
navBarInfo,
navBarBoundingInfo,
systemScreenInfo,
getSystemRectInfo,
}
}
+39
View File
@@ -0,0 +1,39 @@
import { computed, inject, ref, unref } from 'vue'
import { isNumber } from '../../utils'
import type { InjectionKey, Ref } from 'vue'
const zIndex = ref(0)
const defaultInitialZIndex = 2000
export const zIndexContextKey: InjectionKey<Ref<number | undefined>> =
Symbol('zIndexContextKey')
export const useZIndex = () => {
const zIndexInjection = inject(zIndexContextKey, undefined)
const initialZIndex = computed(() => {
const zIndexFromInjection = unref(zIndexInjection)
return isNumber(zIndexFromInjection)
? zIndexFromInjection
: defaultInitialZIndex
})
const currentZIndex = computed(() => initialZIndex.value + zIndex.value)
const nextZIndex = () => {
zIndex.value++
return currentZIndex.value
}
const prevZIndex = () => {
zIndex.value--
return currentZIndex.value
}
return {
initialZIndex,
currentZIndex,
nextZIndex,
prevZIndex,
}
}