Commit 5c798291 authored by shirlyn.guo's avatar shirlyn.guo 🤡

chore: 推荐模板优化

parent 0dc81b62
<script setup lang="ts">
import { computed, onMounted, ref, watch, useTemplateRef, watchEffect } from 'vue'
import { computed, onMounted, ref, watch, useTemplateRef } from 'vue'
import TemplatePreviewModal from './template-preview-modal.vue'
import { useScroll } from '@vueuse/core'
import { DigitalTemplate, TemplateType } from '@/store/types/template'
......@@ -20,8 +20,10 @@ const router = useRouter()
const previewModalVisible = ref(false)
const selectedTemplate = ref<DigitalTemplate>({} as DigitalTemplate)
const checkedClassifyValue = ref('')
const templateList = ref<DigitalTemplate[]>([])
const newTemplateList = ref<(DigitalTemplate & { position: { top: number; left: number } })[]>([])
const templateList = ref<(DigitalTemplate & { position?: { top: number; left: number } })[]>([])
const alreadyCountPositionsList: { id: number; top: number; left: number; height: number; columnIndex: number }[] = []
const pagingInfo = ref({
pageNo: 1,
totalPages: 1,
......@@ -30,9 +32,8 @@ const pagingInfo = ref({
})
const columnHeights = ref<number[]>([])
const waterfallHeight = ref(900)
const templateClassifyIsLoading = ref(false)
const templateClassifyIsLoading = ref(true)
const templateBottomIsLoading = ref(false)
const smooth = ref(false)
const behavior = computed(() => (smooth.value ? 'smooth' : 'auto'))
......@@ -48,22 +49,25 @@ const templateClassify = [
watch(checkedClassifyValue, () => {
templateClassifyIsLoading.value = true
templateList.value = []
newTemplateList.value = []
pagingInfo.value.pageNo = 1
alreadyCountPositionsList.length = 0
columnHeights.value = Array(6).fill(0)
getTemplateList(true)
})
watchEffect(() => {
if (arrivedState.bottom && !templateBottomIsLoading.value) {
if (pagingInfo.value.pageNo < pagingInfo.value.totalPages) {
templateBottomIsLoading.value = true
pagingInfo.value.pageNo += 1
getTemplateList()
watch(
() => arrivedState.bottom,
() => {
if (arrivedState.bottom && !templateBottomIsLoading.value) {
if (pagingInfo.value.pageNo < pagingInfo.value.totalPages) {
templateBottomIsLoading.value = true
pagingInfo.value.pageNo += 1
getTemplateList()
}
}
}
})
},
)
onMounted(() => {
getTemplateList(true)
......@@ -76,50 +80,84 @@ async function getTemplateList(refresh = false) {
}
fetchDigitalHumanTemplateStatusList<DigitalTemplate[]>(checkedClassifyValue.value, pagingInfo.value)
.then((res) => {
.then(async (res) => {
if (res.code !== 0) return
templateList.value = refresh ? res.data : [...templateList.value, ...res.data]
return calculatePositions(templateList.value).then((positions) => {
newTemplateList.value = templateList.value.map((template, index) => ({
...template,
position: positions[index],
}))
pagingInfo.value = res.pagingInfo as PagingInfo
templateClassifyIsLoading.value = false
const newTemplateList = res.data
const newTemplateListPositions = await calculatePositions(newTemplateList, alreadyCountPositionsList)
templateList.value = templateList.value.map((template) => {
const itemWithNoPosition = newTemplateList.find((item) => item.id === template.id)
if (itemWithNoPosition) {
return {
...template,
position: newTemplateListPositions[newTemplateList.indexOf(itemWithNoPosition)],
}
}
return template
})
pagingInfo.value = res.pagingInfo as PagingInfo
})
.finally(() => {
templateClassifyIsLoading.value = false
templateBottomIsLoading.value = false
arrivedState.bottom = false
})
}
async function calculatePositions(templates: any[]) {
const columnHeights = Array(6).fill(0)
const positions: { top: any; left: number; height: unknown }[] = []
const heightPromises = templates.map(async (template) => {
return await calculateImageHeight(template)
async function calculatePositions(
templates: any[],
alreadyPositionsList: { id: number; top: number; left: number; height: number }[],
) {
const columnWidths = 170
const columnHeights = Array(6).fill(0)
const positions: { id: number; top: number; left: number; height: number; columnIndex: number }[] = []
const imgHeights = await Promise.all(
templates.map(async (template) => ({
id: template.id,
imgHeight: await calculateImageHeight(template),
})),
)
alreadyPositionsList.forEach((pos) => {
const columnIndex = Math.floor(pos.left / columnWidths)
columnHeights[columnIndex] = Math.max(columnHeights[columnIndex], pos.top + 20 + pos.height)
})
const imgHeights = await Promise.all(heightPromises)
imgHeights.forEach((imgHeight) => {
const minColumnIndex = getMincColumnHeight(columnHeights)
imgHeights.forEach(({ id, imgHeight }) => {
const minColumnIndex = getMinColumnHeight(columnHeights)
const itemTop = columnHeights[minColumnIndex]
let itemLeft = minColumnIndex * 170
if (itemLeft !== 0) itemLeft += 20
if (itemLeft === 360) itemLeft += 20
if (itemLeft === 530) itemLeft += 40
if (itemLeft === 700) itemLeft += 60
if (itemLeft === 870) itemLeft += 80
const itemLeft = minColumnIndex * columnWidths + minColumnIndex * 20
columnHeights[minColumnIndex] += imgHeight + 20
waterfallHeight.value = Math.max(...columnHeights)
positions.push({ top: itemTop, left: itemLeft, height: imgHeight })
positions.push({ id, top: itemTop, left: itemLeft, height: imgHeight, columnIndex: minColumnIndex })
})
alreadyCountPositionsList.push(...positions)
const columnMap = new Map<number, { id: number; top: number; left: number; height: number; totalValue: number }[]>()
alreadyCountPositionsList.forEach(({ id, top, left, height, columnIndex }) => {
if (!columnMap.has(columnIndex)) {
columnMap.set(columnIndex, [])
}
columnMap.get(columnIndex)?.push({ id, top, left, height, totalValue: top + height })
})
alreadyCountPositionsList.length = 0
columnMap.forEach((items, columnIndex) => {
const maxItem = items.reduce((prev, current) => (prev.totalValue > current.totalValue ? prev : current))
alreadyCountPositionsList.push({
id: maxItem.id,
top: maxItem.top,
left: maxItem.left,
height: maxItem.height,
columnIndex,
})
})
return positions
......@@ -134,6 +172,11 @@ function calculateImageHeight(template: { videoParams: { width: number; height:
})
}
function getMinColumnHeight(arr: number[]) {
let min = Math.min(...arr)
return arr.indexOf(min)
}
function handleOpenPreviewModal(template: DigitalTemplate) {
if (template) {
selectedTemplate.value = { ...template }
......@@ -141,11 +184,6 @@ function handleOpenPreviewModal(template: DigitalTemplate) {
}
}
function getMincColumnHeight(arr: number[]) {
let min = Math.min(...arr)
return arr.indexOf(min)
}
function handleToCreation(template: DigitalTemplate) {
router.push({
name: 'Creation',
......@@ -153,6 +191,7 @@ function handleToCreation(template: DigitalTemplate) {
})
}
</script>
<template>
<div class="sticky top-0 z-10 mx-auto mt-[16px] min-h-[900px] overscroll-contain rounded-t-[16px] bg-white">
<div class="sticky top-[-1px] z-10 rounded-t-[16px] bg-white px-[24px] pb-[16px] pt-[24px]">
......@@ -179,36 +218,37 @@ function handleToCreation(template: DigitalTemplate) {
class="page-main relative h-auto min-h-[900px] w-full"
:style="{ height: waterfallHeight + 'px' }"
>
<div
v-for="item in newTemplateList"
:key="item.id"
:style="{ position: 'absolute', top: item.position.top + 'px', left: item.position.left + 'px' }"
>
<n-card class="mb-[20px] box-content w-[170px] overflow-hidden">
<template #cover>
<div class="relative flex h-auto w-[170px] items-center justify-center overflow-hidden bg-[#f0f0f0]">
<img
:src="item.coverUrl"
class="duration-400 h-auto w-full transform cursor-pointer object-cover transition-transform ease-in-out"
@click="handleOpenPreviewModal(item)"
/>
<div
class="overlay pointer-events-none absolute hidden h-full w-[100%] transform cursor-pointer bg-[#000000]/[.1]"
>
<div v-for="item in templateList" :key="item.id">
<div
v-if="item.position"
:style="{ position: 'absolute', top: item.position!.top + 'px', left: item.position!.left + 'px' }"
>
<n-card class="mb-[20px] box-content w-[170px] overflow-hidden">
<template #cover>
<div class="relative flex h-auto w-[170px] items-center justify-center overflow-hidden bg-[#f0f0f0]">
<img
:src="item.demonstrationGifUrl"
class="duration-400 hover:scale-104 absolute bottom-0 right-0 h-auto w-[50px]"
:src="item.coverUrl"
class="duration-400 h-auto w-full transform cursor-pointer object-cover transition-transform ease-in-out"
@click="handleOpenPreviewModal(item)"
/>
<div
class="overlay pointer-events-none absolute hidden h-full w-[100%] transform cursor-pointer bg-[#000000]/[.1]"
>
<img
:src="item.demonstrationGifUrl"
class="duration-400 hover:scale-104 absolute bottom-0 right-0 h-auto w-[50px]"
/>
</div>
<div
class="overlay duration-400 absolute bottom-2 left-2 right-2 hidden h-8 transform cursor-pointer rounded-[6px] border bg-white/90 px-2 text-center text-[14px] leading-[30px] text-[#000000]"
@click="handleToCreation(item)"
>
做同款
</div>
</div>
<div
class="overlay duration-400 absolute bottom-2 left-2 right-2 hidden h-8 transform cursor-pointer rounded-[6px] border bg-white/90 px-2 text-center text-[14px] leading-[30px] text-[#000000]"
@click="handleToCreation(item)"
>
做同款
</div>
</div>
</template>
</n-card>
</template>
</n-card>
</div>
</div>
</div>
<div class="flex justify-center">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment