import { getCurrentInstance, nextTick, ref, watch , computed} from 'vue' import { useSelectorQuery } from '../../../../hooks' import { cloneDeep, debugWarn, generateId } from '../../../../utils' import type { WaterFallProps,Insertion } from '../water-fall' const processWithInsertions = ( original: any[], insertions: Array = [] ) => { const clone = [...original] // 添加原始索引标记 clone.forEach((item: any, index: number) => (item.__originalIndex = index)); [...insertions].sort((a:Insertion, b:Insertion) => b.index - a.index) .forEach(({ index, slot }) => { const safePos = Math.max(0, Math.min(index, clone.length)) clone.splice(safePos, 0, { __is_insertion: true, slot, __insertPosition: safePos }) }); return clone } export const useWaterFall = (props: WaterFallProps) => { const instance = getCurrentInstance() if (!instance) { debugWarn('TnWaterFall', '请在 setup 中使用 useWaterFall') } const componentId = `twf-${generateId()}` const { getSelectorNodeInfo } = useSelectorQuery(instance) // 切割左右两边数据 const leftData = ref([]) const rightData = ref([]) // 获取左右两边容器的高度信息 let leftContainerHeight = 0 let rightContainerHeight = 0 const getContainerHeight = async () => { try { const leftContainerRectInfo = await getSelectorNodeInfo( `#${componentId}-left` ) const rightContainerRectInfo = await getSelectorNodeInfo( `#${componentId}-right` ) leftContainerHeight = leftContainerRectInfo.height || leftContainerHeight rightContainerHeight = rightContainerRectInfo.height || rightContainerHeight } catch (err) { debugWarn('TnWaterFall', `获取容器高度信息失败:${err}`) } } // 旧数据 let oldUserData: any[] = [] // 分割数据 const splitData = async (data: any[]) => { if (!data || !data.length) return if (props.mode === 'calc') { // 根据左右两边的数据高度,判断当前数据应该放在左边还是右边 await getContainerHeight() if (leftContainerHeight <= rightContainerHeight) { leftData.value.push(data.shift()) } else { rightData.value.push(data.shift()) } nextTick(() => { setTimeout(() => { splitData(data) }, 200) }) } else if (props.mode === 'normal') { // 判断当前的第一个元素是放在左边还是右边 let firstLeft = true await getContainerHeight() if (leftData.value.length > rightData.value.length) { firstLeft = false } let leftSmall = false if (leftContainerHeight < rightContainerHeight) { leftSmall = true } // 按照顺序,左右交替放置数据 data.forEach((item, index) => { if ((index % 2 === 0 && firstLeft) || leftSmall) { leftData.value.push(item) } else { rightData.value.push(item) } if (!firstLeft) { firstLeft = true } if (leftSmall && index >= 2) { leftSmall = false } }) } } // 重新渲染列表 const resetWaterFall = () => { if (!props.data) return leftData.value = [] rightData.value = [] leftContainerHeight = 0 rightContainerHeight = 0 nextTick(() => { oldUserData = props.data splitData(props.data) }) } watch( () => props.data, (val) => { if (!val) return if (oldUserData.length === val.length) return const newData = cloneDeep(val.slice(oldUserData.length)) if (!newData.length) { leftData.value = [] rightData.value = [] leftContainerHeight = 0 rightContainerHeight = 0 } nextTick(() => { oldUserData = val splitData(newData) }) }, { immediate: true, } ) // 转换左边插槽数据 const processedLeftData = computed(() => { return processWithInsertions(leftData.value, props.insertions?.left) }) // 转换右边插槽数据 const processedRightData = computed(() => { return processWithInsertions(rightData.value, props.insertions?.right) }) return { componentId, resetWaterFall, processedLeftData, processedRightData, } }