import { getCurrentInstance, nextTick, provide, reactive, ref, toRefs, watch, } from 'vue' import { tabbarContextKey } from '../../../../tokens' import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../../../../constants' import { useOrderedChildren, useSelectorQuery } from '../../../../hooks' import { debugWarn, generateId, isBoolean, isPromise } from '../../../../utils' import type { TabbarItemContext } from '../../../../tokens' import type { TabbarProps } from '../tabbar' import type { TabbarItemRect } from '../types' export const useTabbar = (props: TabbarProps) => { const { emit } = getCurrentInstance()! const { children: items, addChild, removeChild: removeItem, } = useOrderedChildren() const { getSelectorNodeInfo } = useSelectorQuery() const rectId = `tt-${generateId()}` const activeUid = ref(-1) // 添加Item const addItem = (item: TabbarItemContext) => { if (props.modelValue !== undefined && activeUid.value === -1) { if ( props.modelValue === item.name || props.modelValue === items.value.length ) { nextTick(() => { updateActiveId(item.uid) setTimeout(() => { if (activeUid.value !== -1) return activeUid.value = item.uid }, 50) }) } } addChild(item) } // 更新当前激活的Uid const updateActiveId = (uid: number, changeEmit = false) => { activeUid.value = uid const itemIndex = items.value.findIndex((item) => item.uid === uid) const value = items.value[itemIndex]?.name || itemIndex emit(UPDATE_MODEL_EVENT, value) if (changeEmit) { nextTick(() => { emit(CHANGE_EVENT, value) }) } } // 设置当前被点击Item const setActiveItem = (uid: number) => { // 是否有切换拦截 if (!props.beforeSwitch) { updateActiveId(uid, true) return } const index = items.value.findIndex((item) => item.uid === uid) const shouldSwitch = props.beforeSwitch(index) const isPromiseOrBoolean = [ isPromise(shouldSwitch), isBoolean(shouldSwitch), ].includes(true) if (!isPromiseOrBoolean) { debugWarn( 'TnTabbar', 'beforeSwitch切换前拦截函数必须返回Promise或者Boolean' ) return } if (isPromise(shouldSwitch)) { shouldSwitch.then((res) => { if (res) updateActiveId(uid, true) }) } else { if (shouldSwitch) updateActiveId(uid, true) } } // 根据modelValue设置当前激活的Item const setActiveItemByValue = (value?: string | number) => { if (value === undefined) { // 如果没有传递任何值则设置第一个Item为激活状态 updateActiveId(items.value[0].uid) return } let item: TabbarItemContext | undefined // 如果类型是number,则先通过索引进行查找 if (typeof value === 'number') { item = items.value?.[value] } // 如果没有找到,则通过name查找 if (!item) { item = items.value.find((item) => item.name === value) } if (!item) { // 设置第一个Item为激活状态 updateActiveId(items.value[0].uid) } else { updateActiveId(item.uid) } } watch( () => props.modelValue, (val) => { nextTick(() => { setActiveItemByValue(val) }) } ) // 凸起按钮节点信息 const bulgeRectInfo = ref({ width: 0, height: 0, left: 0, }) // 是否设置凸起按钮 const hasBulgeButton = ref(false) // 设置凸起按钮的位置 const setBulgeCircle = async (itemRectInfo: TabbarItemRect) => { const { left } = itemRectInfo try { const rectInfo = await getSelectorNodeInfo(`#${rectId}`) const { left: tabbarRectLeft } = rectInfo let width = itemRectInfo.width if (itemRectInfo?.maxWidth) { width = itemRectInfo.maxWidth } bulgeRectInfo.value.width = width * props.bulgeSizePercent bulgeRectInfo.value.height = bulgeRectInfo.value.width bulgeRectInfo.value.left = left - (tabbarRectLeft || 0) + itemRectInfo.width / 2 hasBulgeButton.value = true } catch (err) { debugWarn('TnTabbar', `获取Tabbar节点信息失败: ${err}`) } } // provide provide( tabbarContextKey, reactive({ ...toRefs(props), items, activeUid, addItem, removeItem, setActiveItem, setBulgeCircle, }) ) return { rectId, hasBulgeButton, bulgeRectInfo, setActiveItem, setActiveItemByValue, } }