212 lines
6.8 KiB
Vue
212 lines
6.8 KiB
Vue
<script setup lang="ts">
|
|
import { commonApi } from '@/api/common'
|
|
import { useRouter } from 'vue-router'
|
|
|
|
const router = useRouter()
|
|
|
|
definePageMeta({
|
|
layout: 'planet',
|
|
})
|
|
const listParams = ref({
|
|
communityTag: null as string | null,
|
|
pageNum: 1,
|
|
pageSize: 12,
|
|
})
|
|
const containerRef = ref<HTMLElement | undefined>(undefined)
|
|
// 标签
|
|
interface CommunityTag {
|
|
dictLabel: string
|
|
dictValue: string | null
|
|
}
|
|
const communityTagList = ref<CommunityTag[]>([])
|
|
async function getDictType() {
|
|
try {
|
|
const res = await commonApi.dictType({ type: 'community_tag' })
|
|
if (res.code === 200) {
|
|
communityTagList.value = [{
|
|
dictLabel: '全部',
|
|
dictValue: null,
|
|
}, ...res.data] as CommunityTag[]
|
|
}
|
|
}
|
|
catch (error) {
|
|
console.error(error)
|
|
}
|
|
}
|
|
function changeTag(type: string | null) {
|
|
listParams.value.communityTag = type
|
|
initGetList()
|
|
}
|
|
getDictType()
|
|
|
|
interface ApiResponse<T> {
|
|
code: number
|
|
rows: T[]
|
|
total: number
|
|
}
|
|
|
|
interface CommunityItem {
|
|
imageUrl: string
|
|
publishNum: number
|
|
communityName: string
|
|
avatar: string
|
|
createBy: string
|
|
createDay: number
|
|
description: string
|
|
price: number | null
|
|
}
|
|
|
|
// 获取列表
|
|
const listFinish = ref(false)
|
|
const loading = ref(false)
|
|
const dataList = ref<CommunityItem[]>([])
|
|
function initGetList() {
|
|
listFinish.value = false
|
|
listParams.value.pageNum = 1
|
|
getList()
|
|
}
|
|
async function getList() {
|
|
try {
|
|
if (listFinish.value || loading.value) // 添加loading检查,避免重复请求
|
|
return
|
|
loading.value = listParams.value.pageNum === 1 // 只在第一页显示loading
|
|
const res = await request.post<ApiResponse<CommunityItem>>('/community/list', listParams.value)
|
|
if (res.code === 200) {
|
|
if (listParams.value.pageNum === 1) {
|
|
dataList.value = res.rows
|
|
}
|
|
else {
|
|
dataList.value = [...dataList.value, ...res.rows]
|
|
}
|
|
|
|
if (dataList.value.length >= res.total) {
|
|
listFinish.value = true
|
|
}
|
|
listParams.value.pageNum++
|
|
}
|
|
}
|
|
catch (error) {
|
|
console.error(error)
|
|
}
|
|
finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
getList()
|
|
|
|
function goDetail(id: string) {
|
|
router.push(`/planet-detail/${id}`)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="min-h-screen bg-[#f7f8fa] relative">
|
|
<n-affix
|
|
:trigger-top="0"
|
|
position="absolute"
|
|
:listen-to="() => containerRef"
|
|
>
|
|
<div class="px-10 bg-[#fff] sticky top-0 left-0 right-0 z-10">
|
|
<div class="flex flex-wrap gap-2 py-4">
|
|
<div
|
|
v-for="(item, index) in communityTagList"
|
|
:key="index"
|
|
class="px-[12px] py-[9px] text-sm rounded-lg cursor-pointer"
|
|
:class="{ 'bg-black text-white': listParams.communityTag === item.dictValue, 'bg-white text-[#878d95] hover:bg-gray-100': listParams.communityTag !== item.dictValue }"
|
|
@click="changeTag(item.dictValue)"
|
|
>
|
|
{{ item.dictLabel }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</n-affix>
|
|
|
|
<article class="px-10 py-6">
|
|
<n-infinite-scroll :distance="10" trigger="once" @load="getList">
|
|
<!-- 添加trigger="once"属性 -->
|
|
<div v-if="loading" class="grid grid-cols-3 gap-2">
|
|
<div v-for="i in 9" :key="i" class="bg-white rounded-lg overflow-hidden shadow-sm flex p-4">
|
|
<n-skeleton class="w-[161px] h-[161px] rounded-lg flex-shrink-0" />
|
|
<div class="flex flex-col gap-2 px-4 flex-1">
|
|
<div class="flex flex-col gap-2">
|
|
<n-skeleton text :width="140" />
|
|
<div class="flex items-center gap-2">
|
|
<n-skeleton circle width="20px" height="20px" />
|
|
<n-skeleton text :width="60" />
|
|
<n-skeleton text :width="80" />
|
|
</div>
|
|
<n-skeleton text :repeat="2" />
|
|
</div>
|
|
<n-skeleton text :width="60" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else-if="dataList.length > 0" class="grid grid-cols-3 gap-2">
|
|
<PlanetItem v-for="(item, index) in dataList" :key="index" :item="item" type="all" />
|
|
<!--
|
|
<div v-for="(item, index) in dataList" :key="index" class="bg-white rounded-lg overflow-hidden shadow-sm flex p-4 cursor-pointer hover:shadow-[0_4px_14px_0_rgba(0,0,0,0.1)] hover:-translate-y-1 transition-all duration-300" @click="goDetail(item.id)">
|
|
<div class="w-[161px] h-[161px] relative rounded-lg">
|
|
<img class="w-full h-full object-cover rounded-lg" :src="item.imageUrl" alt="">
|
|
<span class="absolute bottom-2 left-2 text-white text-xs">{{ item.publishNum || 0 }}人已经加入</span>
|
|
</div>
|
|
<div class="flex flex-col gap-2 px-4 flex-1 justify-between">
|
|
<div class="flex flex-col gap-2">
|
|
<div class="text-base font-bold">
|
|
{{ item.communityName }}
|
|
</div>
|
|
<div class="flex items-center gap-2 text-xs text-[#878d95]">
|
|
<img class="w-[20px] h-[20px] rounded-full" :src="item.avatar" alt="">
|
|
<div>
|
|
{{ item.nickName }}
|
|
</div>
|
|
<div>创建{{ item.createDay || 0 }}天</div>
|
|
</div>
|
|
<div class="text-sm text-[#4a5563] line-clamp-3">
|
|
{{ item.description }}
|
|
</div>
|
|
</div>
|
|
<div class="text-[#fc4141]">
|
|
<div v-if="item.price">
|
|
<span class="text-base font-bold mr-1">
|
|
{{ item.price }}
|
|
</span>
|
|
<span class="text-xs">
|
|
金币
|
|
</span>
|
|
</div>
|
|
<div v-else>
|
|
免费
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> -->
|
|
</div>
|
|
<div v-if="dataList.length === 0 && listFinish" class="flex flex-col items-center justify-center py-12">
|
|
<div class="w-48 h-48 mb-4 flex items-center justify-center">
|
|
<div class="relative">
|
|
<div class="w-32 h-32 rounded-full border-4 border-gray-200" />
|
|
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-24 h-24 rounded-full border-4 border-gray-100" />
|
|
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-16 h-16 rounded-full bg-gray-50" />
|
|
</div>
|
|
</div>
|
|
<p class="text-gray-500 mb-4">
|
|
这里空空如也,什么都没有找到~
|
|
</p>
|
|
<!-- <n-button type="primary" size="small" class="px-6">
|
|
去发现更多
|
|
</n-button> -->
|
|
</div>
|
|
</n-infinite-scroll>
|
|
</article>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.line-clamp-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
</style>
|