You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
paopao-ce/web/src/views/Post.vue

274 lines
7.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div>
<main-nav title="泡泡详情" :back="true" />
<n-list class="main-content-wrap" bordered>
<n-list-item>
<n-spin :show="loading">
<div class="detail-wrap" v-if="post.id > 1">
<post-detail :post="post" @reload="loadPost" />
</div>
<div class="empty-wrap" v-else>
<n-empty size="large" description="暂无数据" />
</div>
</n-spin>
</n-list-item>
<div class="comment-opts-wrap" v-if="post.id > 0">
<n-tabs type="bar" justify-content="end" size="small" animated @update:value="commentTab">
<template #prefix>
<span class="comment-title-item"></span>
</template>
<n-tab-pane name="default" tab="默认" />
<n-tab-pane name="newest" tab="最新" />
</n-tabs>
</div>
<n-list-item v-if="post.id > 0">
<compose-comment :lock="post.is_lock" :post-id="post.id" @post-success="reloadComments" />
</n-list-item>
<div v-if="post.id > 0">
<div v-if="commentLoading" class="skeleton-wrap">
<post-skeleton :num="5" />
</div>
<div v-else>
<div class="empty-wrap" v-if="comments.length === 0">
<n-empty size="large" description="暂无评论,快来抢沙发" />
</div>
<n-list-item v-for="comment in comments" :key="comment.id">
<comment-item :comment="comment" @reload="reloadComments" />
</n-list-item>
</div>
</div>
<n-space justify="center">
<InfiniteLoading class="load-more" v-if="comments.length > 0" :slots="{complete: '没有更多数据了', error: '加载出错'}" @infinite="loadComments">
<template #spinner>
<span v-if="defaultCommentsSort && defaultNoMore" class="load-more-spinner" ><!-- --></span>
<span v-if="!defaultCommentsSort && newestNoMore" class="load-more-spinner" ><!-- --></span>
<span v-if="defaultCommentsSort && !defaultNoMore" class="load-more-spinner" ></span>
<span v-if="!defaultCommentsSort && !newestNoMore" class="load-more-spinner" ></span>
</template>
</InfiniteLoading>
</n-space>
</n-list>
</div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted, computed } from 'vue';
import { useRoute } from 'vue-router';
import { getPost, getPostComments } from '@/api/post';
import InfiniteLoading from "v3-infinite-loading";
import "v3-infinite-loading/lib/style.css";
const route = useRoute();
const post = ref<Item.PostProps>({} as Item.PostProps);
const loading = ref(false);
const commentLoading = ref(false);
const comments = ref<Item.CommentProps[]>([]);
const postId = computed(() => +(route.query.id as string));
const sortStrategy = ref<"default" | "newest">('default');
const defaultCommentsSort = ref<boolean>(true)
const pageSize = 20
let stateHandler = ({
loading() {
//nothing
},
loaded() {
// nothing
},
complete() {
// nothing
},
error() {
// nothing
},
});
const commentTab = (tab: "default" | "newest") => {
sortStrategy.value = tab;
if (tab === "default") {
defaultCommentsSort.value = true
}
loadComments(stateHandler);
};
const loadPost = () => {
post.value = {
id: 0,
} as Item.PostProps;
loading.value = true;
getPost({
id: postId.value,
})
.then((res) => {
loading.value = false;
post.value = res;
// 加载评论
loadComments(stateHandler);
})
.catch((err) => {
loading.value = false;
});
};
let defaultCommmentsPage = 1;
const defaultNoMore = ref<boolean>(false)
const defaultComments = ref<Item.CommentProps[]>([]);
const loadDefaultComments = ($state: any) => {
if (defaultNoMore.value) {
return
}
getPostComments({
id: post.value.id as number,
sort_strategy: 'default',
page: defaultCommmentsPage,
page_size: pageSize,
})
.then((res) => {
if ($state !== null) {
stateHandler = $state
}
if (res.list.length < pageSize) {
defaultNoMore.value = true
} else {
defaultCommmentsPage++
}
if (res.list.length > 0) {
if (defaultCommmentsPage === 1) {
defaultComments.value = res.list;
} else {
defaultComments.value.push(...res.list);
}
comments.value = defaultComments.value
}
stateHandler.loaded();
commentLoading.value = false;
})
.catch((err) => {
commentLoading.value = false;
stateHandler.error();
});
};
let newestCommmentsPage = 1;
let newestNoMore = ref<boolean>(false)
const newestComments=ref<Item.CommentProps[]>([]);
const loadNewestComments = ($state: any) => {
if (newestNoMore.value) {
return
}
getPostComments({
id: post.value.id as number,
sort_strategy: 'newest',
page: newestCommmentsPage,
page_size: pageSize,
})
.then((res) => {
if ($state !== null) {
stateHandler = $state
}
if (res.list.length < pageSize) {
newestNoMore.value = true
} else {
newestCommmentsPage++
}
if (res.list.length > 0) {
if (newestCommmentsPage === 1) {
newestComments.value = res.list;
} else {
newestComments.value.push(...res.list);
}
comments.value = newestComments.value
}
stateHandler.loaded();
commentLoading.value = false;
})
.catch((err) => {
commentLoading.value = false;
stateHandler.error();
});
};
const loadComments = ($state: any) => {
if (postId.value < 1) {
return
}
if (comments.value.length === 0) {
commentLoading.value = true;
}
if (sortStrategy.value === 'default') {
comments.value = defaultComments.value
loadDefaultComments($state)
} else {
comments.value = newestComments.value
loadNewestComments($state)
}
commentLoading.value = false;
};
const reloadComments = () => {
// 这里需要做特殊处理,目前暴力处理,一切都重新加载
// TODO后续持续优化 这里有大bug
defaultCommmentsPage = 1;
defaultNoMore.value = false
defaultComments.value = []
newestCommmentsPage = 1;
newestNoMore.value = false
newestComments.value = []
loadComments(stateHandler)
}
onMounted(() => {
loadPost();
});
watch(postId, () => {
if (postId.value > 0 && route.name === 'post') {
loadPost();
}
});
</script>
<style lang="less" scoped>
.detail-wrap {
min-height: 100px;
}
.comment-opts-wrap {
padding-top: 6px;
padding-left: 16px;
padding-right: 16px;
opacity: 0.75;
.comment-title-item {
padding-top: 4px;
font-size: 16px;
text-align: center;
}
}
.main-content-wrap {
.load-more {
margin-bottom: 8px;
.load-more-spinner {
font-size: 14px;
opacity: 0.65;
}
}
}
.dark {
.main-content-wrap,
.skeleton-wrap {
background-color: rgba(16, 16, 20, 0.75);
}
}
</style>