mcwl-pc/app/components/ModelList.vue

183 lines
4.9 KiB
Vue

<template>
<div class="flex flex-wrap justify-center">
<div class="grid grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-4 p-4">
<div
v-for="item in dataList"
:key="item.id"
class="relative rounded-lg overflow-hidden"
>
<!-- 图片 -->
<div class="relative border border-solid border-[#e5e7eb] h-[300px] overflow-hidden rounded-lg" @click="toDetail(item)">
<img
:src="item.surfaceUrl"
class="w-full h-full object-cover rounded-lg cursor-pointer ransform transition-transform duration-300 hover:scale-110"
alt=""
/>
<!-- 左上角标签 -->
<div
class="absolute top-2 left-2 bg-black/50 text-white text-xs px-2 py-1 rounded"
>
{{ item.type }}
</div>
<!-- 底部数据统计 -->
<div
class="absolute bottom-0 left-0 right-0 flex items-center gap-2 p-2 text-xs text-white"
>
<div class="flex items-center">
<component :is="Play" class="h-[14px] w-[14px] text-white menu-icon m-1" />
{{ item.reals || 0 }}
</div>
<div class="flex items-center">
<component
:is="Download"
class="h-[14px] w-[14px] text-white menu-icon m-1"
/>
{{ item.numbers || 0 }}
</div>
</div>
</div>
<!-- 作者信息条 -->
<div class="mt-1 px-2 py-1">
<div>
<span class="text-[#19191c] text-[14px]">
{{ item.modelName }}
</span>
</div>
<div class="flex mt-1">
<img :src="item.avatar" class="w-4 h-4 rounded-full mr-2" alt="" />
<span class="text-xs text-gray-500 truncate">{{ item.nickName }}</span>
</div>
</div>
</div>
<div v-if="!loading && dataList.length === 0">
<Empty></Empty>
</div>
</div>
<div
ref="loadingTrigger"
class="h-20 w-[1000px] text-center text-gray-500 flex justify-center items-center"
>
<div v-if="loading">加载中...</div>
<div v-if="finished && dataList.length >= 20">没有更多数据了</div>
</div>
</div>
</template>
<script setup lang="ts">
import { Download, Play } from "lucide-vue-next";
import { nextTick, onMounted, onUnmounted, ref } from "vue";
const emit = defineEmits(['modelTotal'])
const loading = ref(false);
const finished = ref(false);
const total = ref(0); //
const loadingTrigger = ref(null);
const observer = ref<IntersectionObserver | null>(null);
const props = defineProps({
params: {
type: Object,
default: () => {},
},
});
const listParams = ref({
...props.params,
pageNumber: 1,
pageSize: 20,
});
function initPageNUm() {
listParams.value.pageNumber = 1;
finished.value = false; // 重置加载完成状态
listParams.value = Object.assign({}, listParams.value, props.params);
getDataList();
}
const dataList = ref([]);
async function getDataList() {
if (loading.value || finished.value) return;
loading.value = true;
try {
const res = await request.post("/model/modelSquare", { ...listParams.value });
if (res.code === 200) {
// 如果是第一页,直接赋值,否则追加数据
if (listParams.value.pageNumber === 1) {
dataList.value = res.data.list;
} else {
dataList.value = [...dataList.value, ...res.data.list];
}
total.value = res.data.total; // 假设接口返回了总条数
// 判断是否加载完所有数据
if (dataList.value.length >= total.value) {
finished.value = true;
}
// 自动增加页码
listParams.value.pageNumber++;
emit('modelTotal', total.value)
}
} catch (err) {
dataList.value = [];
finished.value = true;
console.log(err);
} finally {
loading.value = false;
}
}
getDataList();
// 跳转详情
function toDetail(item:any){
// router.push(`/model-details/${item.id}`)
const baseUrl = window.location.origin
window.open(`${baseUrl}/model-details/${item.id}`, '_blank', 'noopener,noreferrer')
}
onMounted(() => {
window.addEventListener("scroll", topedRefresh);
observer.value = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && !loading.value && !finished.value) {
getDataList();
}
},
{
threshold: 0.1,
}
);
if (loadingTrigger.value) {
observer.value.observe(loadingTrigger.value);
}
});
onUnmounted(() => {
window.removeEventListener("scroll", topedRefresh);
if (observer.value) {
observer.value.disconnect();
}
});
async function topedRefresh() {
if (import.meta.client) {
await nextTick();
window.scrollTo({
top: 0,
behavior: "smooth",
});
}
initPageNUm();
}
defineExpose({
initPageNUm,
});
</script>
<style scoped></style>