183 lines
4.9 KiB
Vue
183 lines
4.9 KiB
Vue
<template>
|
|
<div class="w-full">
|
|
<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] max-w-xs truncate ">
|
|
{{ 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>
|
|
<div class="w-full flex justify-center" v-if="!loading && dataList.length === 0">
|
|
<Empty></Empty>
|
|
</div>
|
|
<div
|
|
ref="loadingTrigger"
|
|
class="h-20 w-full 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>
|