@tuniao/tnui-vue3-uniapp
Version:
TuniaoUI for vue3 uniapp
155 lines (143 loc) • 4.37 kB
text/typescript
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<Insertion> = []
) => {
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<any[]>([])
const rightData = ref<any[]>([])
// 获取左右两边容器的高度信息
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,
}
}