This commit is contained in:
2026-01-05 12:47:14 +08:00
commit 1fc846fae3
1614 changed files with 162035 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
export * from './useExpose'
export * from './useGlobalZIndex'
export * from './useInject'
export * from './useLockScroll'
export * from './useProvide'
export * from './useRect'
export * from './useRelation'
export * from './useRouter'
export * from './useSelectorQuery'
export * from './useStyle'
export * from './useTouch'

View File

@@ -0,0 +1,8 @@
import type { ComponentPublicInstance } from 'vue'
import { getCurrentInstance } from 'vue'
export function useExpose(apis: Record<string, any>) {
const instance = getCurrentInstance()
if (instance)
Object.assign(instance.proxy as ComponentPublicInstance, apis)
}

View File

@@ -0,0 +1,9 @@
let globalZIndex = 2000
export function useGlobalZIndex() {
return ++globalZIndex
}
export function setGlobalZIndex(value: number) {
globalZIndex = value
}

View File

@@ -0,0 +1,32 @@
import { computed, getCurrentInstance, inject, onUnmounted, ref } from 'vue'
import type { ComponentInternalInstance, InjectionKey } from 'vue'
type ParentProvide<T> = T & {
add: (child: ComponentInternalInstance) => void
remove: (child: ComponentInternalInstance) => void
internalChildren: ComponentInternalInstance[]
}
export function useInject<T>(key: InjectionKey<ParentProvide<T>>) {
const parent = inject(key, null)
if (parent) {
const instance = getCurrentInstance()!
const { add, remove, internalChildren } = parent
add(instance)
onUnmounted(() => remove(instance))
const index = computed(() => internalChildren.indexOf(instance))
return {
parent,
index,
}
}
return {
parent: null,
index: ref(-1),
}
}

View File

@@ -0,0 +1,31 @@
let count = 0
const CLSNAME = 'nut-overflow-hidden'
export function useLockScroll(isLock: () => boolean) {
const lock = () => {
if (isLock()) {
try {
!count && document.body.classList.add(CLSNAME)
count++
}
catch (error) {
console.error(error)
}
}
}
const unlock = () => {
if (isLock() && count) {
try {
count--
!count && document.body.classList.remove(CLSNAME)
}
catch (error) {
console.error(error)
}
}
}
return [lock, unlock]
}

View File

@@ -0,0 +1,93 @@
import { getCurrentInstance, markRaw, provide, shallowReactive } from 'vue'
import type {
ComponentInternalInstance,
ConcreteComponent,
InjectionKey,
VNode,
VNodeNormalizedChildren,
} from 'vue'
// TODO: uniapp 不支持 vue 直接导出的 isVNode
export function isVNode(value: any): value is VNode {
return value ? value.__v_isVNode === true : false
}
export function flattenVNodes(shouldTraverseChildren: VNodeNormalizedChildren, childName?: string) {
const result: VNode[] = []
const traverse = (children: VNodeNormalizedChildren) => {
if (!Array.isArray(children))
return
children.forEach((child) => {
if (!isVNode(child))
return
if (childName) {
if (child.type && (child.type as ConcreteComponent).name === childName) {
result.push(child)
return
}
}
else {
result.push(child)
}
if (child.component?.subTree)
traverse(child.component.subTree.children)
if (child.children)
traverse(child.children)
})
}
traverse(shouldTraverseChildren)
return result
}
export function sortChildren(
parent: ComponentInternalInstance,
internalChildren: ComponentInternalInstance[],
childName?: string,
) {
const vnodes = flattenVNodes(parent && parent.subTree && parent.subTree.children, childName)
internalChildren.sort((a, b) => {
return vnodes.indexOf(a.vnode) - vnodes.indexOf(b.vnode)
})
}
// 如果指定组件名称,则只查找此组件并且查到后结束。也就是不关心此组件下的内容,在大部分场景下节省查找消耗。
export function useProvide<ProvideValue>(key: InjectionKey<ProvideValue>, childName?: string) {
const internalChildren: ComponentInternalInstance[] = shallowReactive([])
const publicChildren = shallowReactive<any[]>([])
const parent = getCurrentInstance()!
const add = (child: ComponentInternalInstance) => {
if (!child.proxy)
return
internalChildren.push(markRaw(child))
publicChildren.push(markRaw(child.proxy))
sortChildren(parent, internalChildren, childName)
}
const remove = (child: ComponentInternalInstance) => {
if (child.proxy) {
internalChildren.splice(internalChildren.indexOf(markRaw(child)), 1)
publicChildren.splice(publicChildren.indexOf(markRaw(child.proxy)), 1)
}
}
return (value?: ProvideValue) => {
provide(key, {
add,
remove,
internalChildren,
...value,
} as any)
return {
internalChildren,
children: publicChildren,
}
}
}

View File

@@ -0,0 +1,7 @@
import type { ComponentInternalInstance } from 'vue'
import { useSelectorQuery } from './useSelectorQuery'
export function useRect(id: string, instance?: ComponentInternalInstance): Promise<UniApp.NodeInfo> {
const { getSelectorNodeInfo } = useSelectorQuery(instance)
return getSelectorNodeInfo(`#${id}`)
}

View File

@@ -0,0 +1,8 @@
import type { ComponentPublicInstance } from 'vue'
import { getCurrentInstance } from 'vue'
export function useExtend<T>(apis: T) {
const instance = getCurrentInstance()
if (instance)
Object.assign(instance.proxy as ComponentPublicInstance, apis)
}

View File

@@ -0,0 +1,28 @@
export type NavigateToOptions = string | UniApp.NavigateToOptions
export type RedirectToOptions = string | UniApp.RedirectToOptions
export type RouterOptions = UniApp.NavigateToOptions | UniApp.RedirectToOptions
export function useRouter() {
const push = (options: NavigateToOptions) => {
if (typeof options === 'string') {
uni.navigateTo({ url: options })
return
}
uni.navigateTo(options)
}
const replace = (options: RedirectToOptions) => {
if (typeof options === 'string') {
uni.redirectTo({ url: options })
return
}
uni.redirectTo(options)
}
return {
push,
replace,
}
}

View File

@@ -0,0 +1,67 @@
import { getCurrentInstance } from 'vue'
import type { ComponentInternalInstance } from 'vue'
export function useSelectorQuery(instance?: ComponentInternalInstance | null) {
let query: UniApp.SelectorQuery | null = null
if (!instance)
instance = getCurrentInstance()
if (!instance)
console.warn('useSelectorQuery', 'useSelectorQuery必须在setup函数中使用')
// #ifndef MP-ALIPAY
query = uni.createSelectorQuery().in(instance)
// #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,
}
}

View File

@@ -0,0 +1,29 @@
import { computed, normalizeClass, normalizeStyle } from 'vue'
import { stringifyStyle } from '../_utils'
export function useStyleContext(props: any, componentName: string) {
const mainClass = computed(() => {
const cls = normalizeClass([props.customClass, { [componentName]: true }])
return cls
})
const mainStyle = computed(() => {
return stringifyStyle(normalizeStyle(props.customStyle))
})
const getMainClass = (cls: unknown) => {
return normalizeClass([props.customClass, { [componentName]: true }, cls])
}
const getMainStyle = (style: unknown) => {
return stringifyStyle(normalizeStyle([props.customStyle, style]))
}
return {
mainClass,
mainStyle,
getMainClass,
getMainStyle,
}
}

View File

@@ -0,0 +1,74 @@
import { ref } from 'vue'
const MIN_DISTANCE = 10
type Direction = '' | 'vertical' | 'horizontal'
function getDirection(x: number, y: number) {
if (x > y && x > MIN_DISTANCE)
return 'horizontal'
if (y > x && y > MIN_DISTANCE)
return 'vertical'
return ''
}
export function useTouch() {
const startX = ref(0)
const startY = ref(0)
const moveX = ref(0)
const moveY = ref(0)
const deltaX = ref(0)
const deltaY = ref(0)
const offsetX = ref(0)
const offsetY = ref(0)
const direction = ref<Direction>('')
const isVertical = () => direction.value === 'vertical'
const isHorizontal = () => direction.value === 'horizontal'
const reset = () => {
deltaX.value = 0
deltaY.value = 0
offsetX.value = 0
offsetY.value = 0
direction.value = ''
}
const start = ((event: TouchEvent) => {
reset()
startX.value = event.touches[0].clientX
startY.value = event.touches[0].clientY
}) as EventListener
const move = ((event: TouchEvent) => {
const touch = event.touches[0]
deltaX.value = touch.clientX - startX.value
deltaY.value = touch.clientY - startY.value
moveX.value = touch.clientX
moveY.value = touch.clientY
offsetX.value = Math.abs(deltaX.value)
offsetY.value = Math.abs(deltaY.value)
if (!direction.value)
direction.value = getDirection(offsetX.value, offsetY.value)
}) as EventListener
return {
move,
start,
reset,
startX,
startY,
moveX,
moveY,
deltaX,
deltaY,
offsetX,
offsetY,
direction,
isVertical,
isHorizontal,
}
}