feat: ranking page fetch

This commit is contained in:
KUN1007 2023-10-17 19:03:59 +08:00
parent 69f4db0bfe
commit e5e3135aef
15 changed files with 262 additions and 127 deletions

View file

@ -14,50 +14,29 @@ const homeURLs = {
export async function getHomeTopicApi( export async function getHomeTopicApi(
requestData: Home.HomeTopicRequestData requestData: Home.HomeTopicRequestData
): Promise<Home.HomeTopicResponseData> { ): Promise<Home.HomeTopicResponseData> {
try {
const queryParams = objectToQueryParams(requestData) const queryParams = objectToQueryParams(requestData)
// 调用 fetchPost 函数
const response = await fetchGet<Home.HomeTopicResponseData>( const response = await fetchGet<Home.HomeTopicResponseData>(
`${homeURLs.home}?${queryParams}` `${homeURLs.home}?${queryParams}`
) )
return response return response
} catch (error) {
// 处理错误
console.error(error)
throw new Error('Failed to fetch home topic')
}
} }
// 首页今日热门话题 // 首页今日热门话题
export async function getHomeNavHotTopicApi(): Promise<Home.HomeHotTopicResponseData> { export async function getHomeNavHotTopicApi(): Promise<Home.HomeHotTopicResponseData> {
try {
// 调用 fetchPost 函数
const response = await fetchGet<Home.HomeHotTopicResponseData>( const response = await fetchGet<Home.HomeHotTopicResponseData>(
homeURLs.navHot homeURLs.navHot
) )
return response return response
} catch (error) {
// 处理错误
console.error(error)
throw new Error('Failed to fetch home nav hot topic')
}
} }
// 首页今日最新话题 // 首页今日最新话题
export async function getHomeNavNewTopicApi(): Promise<Home.HomeNewTopicResponseData> { export async function getHomeNavNewTopicApi(): Promise<Home.HomeNewTopicResponseData> {
try {
// 调用 fetchPost 函数
const response = await fetchGet<Home.HomeNewTopicResponseData>( const response = await fetchGet<Home.HomeNewTopicResponseData>(
homeURLs.navNew homeURLs.navNew
) )
return response return response
} catch (error) {
// 处理错误
console.error(error)
throw new Error('Failed to fetch home nav new topic')
}
} }

View file

@ -29,12 +29,13 @@ export interface HomeTopic {
tid: number tid: number
title: string title: string
views: number views: number
likes: number[] upvotesCount: number
replies: number[] likesCount: number
repliesCount: number
comments: number comments: number
time: number time: number
content: string content: string
upvotes: number[]
tags: string[] tags: string[]
category: string[] category: string[]
popularity: number popularity: number

View file

@ -8,6 +8,7 @@ export * from './edit/types/edit'
export * from './home/types/home' export * from './home/types/home'
export * from './user/types/user' export * from './user/types/user'
export * from './login/types/login' export * from './login/types/login'
export * from './ranking/types/ranking'
export * from './topic/types' export * from './topic/types'
export * from './update-log/types/updateLog' export * from './update-log/types/updateLog'
@ -17,5 +18,6 @@ export * from './edit'
export * from './home' export * from './home'
export * from './user' export * from './user'
export * from './login' export * from './login'
export * from './ranking'
export * from './topic' export * from './topic'
export * from './update-log' export * from './update-log'

14
src/api/ranking/index.ts Normal file
View file

@ -0,0 +1,14 @@
import { fetchGet } from '@/utils/request'
import objectToQueryParams from '@/utils/objectToQueryParams'
import * as Ranking from './types/ranking'
// 获取 ranking 热门话题
export async function getRankingTopicsApi(
request: Ranking.RankingGetTopicsRequestData
): Promise<Ranking.RankingGetTopicsResponseData> {
const queryParams = objectToQueryParams(request)
const url = `/ranking/topics?${queryParams}`
const response = await fetchGet<Ranking.RankingGetTopicsResponseData>(url)
return response
}

View file

@ -0,0 +1,18 @@
export interface RankingGetTopicsRequestData {
page: number
limit: number
sortField: string
sortOrder: string
}
export interface RankingTopics {
tid: number
title: string
field: string
}
export interface RankingGetUserRequestData {}
export type RankingGetTopicsResponseData = KUNGalgameResponseData<
RankingTopics[]
>

View file

@ -25,7 +25,7 @@ export interface TopicDetail {
tags: string[] tags: string[]
edited: number edited: number
user: TopicUserInfo user: TopicUserInfo
rid: number[] replies: number[]
status: number status: number
share: number[] share: number[]
category: string[] category: string[]

View file

@ -12,11 +12,11 @@ export interface UserInfo {
dislike: number dislike: number
daily_topic_count: number daily_topic_count: number
topic: number[] topicCount: number[]
reply: number[] replyCount: number[]
comment: number[] commentCount: number[]
like_topic: number[] likeTopic: number[]
upvote_topic: number[] upvoteTopic: number[]
} }
// 用户更新头像 // 用户更新头像

View file

@ -0,0 +1,43 @@
// 评论的临时数据,用于组件间传输
import { defineStore } from 'pinia'
import type {
RankingGetTopicsRequestData,
RankingGetTopicsResponseData,
RankingGetUserRequestData,
} from '@/api'
import { getRankingTopicsApi } from '@/api'
interface RankingStore {
topic: RankingGetTopicsRequestData
user: RankingGetUserRequestData
}
export const useKUNGalgameRankingStore = defineStore({
id: 'KUNGalgameRanking',
// 不持久
persist: false,
state: (): RankingStore => ({
topic: {
page: 0,
limit: 0,
sortField: 'popularity',
sortOrder: 'desc',
},
user: {},
}),
getters: {},
actions: {
async getTopics(): Promise<RankingGetTopicsResponseData> {
const requestData: RankingGetTopicsRequestData = {
page: this.topic.page,
limit: this.topic.limit,
sortField: this.topic.sortField,
sortOrder: this.topic.sortOrder,
}
return await getRankingTopicsApi(requestData)
},
},
})

View file

@ -7,7 +7,7 @@ export const isValidURL = (url: string) => {
// 正则表达式匹配合法邮箱 // 正则表达式匹配合法邮箱
export const isValidEmail = (email: string) => { export const isValidEmail = (email: string) => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ const regex = /^[^\s@]{1,64}@[^\s@]{1,255}\.[^\s@]{1,24}$/
return regex.test(email) return regex.test(email)
} }

View file

@ -23,12 +23,12 @@ const {
tid, tid,
title, title,
views, views,
likes, likesCount,
replies, repliesCount,
comments, comments,
time, time,
content, content,
upvotes, upvotesCount,
tags, tags,
category, category,
popularity, popularity,
@ -37,7 +37,7 @@ const {
const plainText = getPlainText(content) const plainText = getPlainText(content)
const getRepliesCount = computed(() => { const getRepliesCount = computed(() => {
return replies.length + comments return repliesCount + comments
}) })
</script> </script>
@ -57,9 +57,9 @@ const getRepliesCount = computed(() => {
<Icon icon="ic:outline-remove-red-eye" /><span>{{ views }}</span> <Icon icon="ic:outline-remove-red-eye" /><span>{{ views }}</span>
</li> </li>
<li> <li>
<Icon icon="line-md:thumbs-up-twotone" /><span>{{ <Icon icon="line-md:thumbs-up-twotone" /><span>
likes.length {{ likesCount }}
}}</span> </span>
</li> </li>
<li> <li>
<Icon icon="ri:reply-line" /><span>{{ getRepliesCount }}</span> <Icon icon="ri:reply-line" /><span>{{ getRepliesCount }}</span>
@ -68,12 +68,14 @@ const getRepliesCount = computed(() => {
<!-- 话题的发布日期 --> <!-- 话题的发布日期 -->
<div class="time"> <div class="time">
<span>{{ <span>
{{
formatTimeDifference( formatTimeDifference(
time, time,
settingsStore.showKUNGalgameLanguage.value settingsStore.showKUNGalgameLanguage.value
) )
}}</span> }}
</span>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,26 +1,44 @@
<script setup lang="ts"> <script setup lang="ts">
import { Icon } from '@iconify/vue' import { Icon } from '@iconify/vue'
import type { RankingTopics } from '@/api'
import { computed } from 'vue'
const props = defineProps<{
field: string
topics: RankingTopics[]
}>()
const topics = computed(() => props.topics)
const icons: Record<string, string> = {
popularity: 'bi:fire',
upvotes: 'bi:rocket',
views: 'ic:outline-remove-red-eye',
likes: 'line-md:thumbs-up-twotone',
replies: 'ri:reply-line',
comments: 'fa-regular:comment-dots',
}
//
const parseTopicNumber = (field: string | string[]) => {
return Array.isArray(field) ? field.length : Math.ceil(parseInt(field))
}
</script> </script>
<template> <template>
<!-- 单个话题 --> <!-- 单个话题 -->
<div class="single-topic"> <div class="single-topic" v-for="(topic, index) in topics" :key="index">
<!-- 话题的名字 --> <!-- 话题的名字 -->
<div class="topic-name"> <div class="topic-name">
啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星 {{ topic.title }}
</div> </div>
<!-- 话题的其它信息 --> <!-- 话题的其它信息 -->
<div class="detail"> <div class="detail">
<!-- 浏览数 --> <!-- 浏览数 -->
<span><Icon icon="ic:outline-remove-red-eye" />1007</span> <span>
<!-- 点赞数 --> <Icon :icon="icons[props.field]" />
<span><Icon icon="line-md:thumbs-up-twotone" />1007</span> {{ parseTopicNumber(topic.field) }}
<!-- 回复数 --> </span>
<span><Icon icon="ri:reply-line" />1007</span>
<!-- 评论数 -->
<span><Icon icon="fa-regular:comment-dots" />1007</span>
<!-- 推话题数 -->
<span><Icon icon="bi:rocket" />1007</span>
</div> </div>
</div> </div>
</template> </template>

View file

@ -1,39 +1,77 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { Icon } from '@iconify/vue'
import Topic from './Topic.vue' import Topic from './Topic.vue'
import { useKUNGalgameRankingStore } from '@/store/modules/ranking'
import { storeToRefs } from 'pinia'
import type { RankingTopics } from '@/api'
import { topicNavSortItem } from './navSortItem'
const { topic, user } = storeToRefs(useKUNGalgameRankingStore())
const topics = ref<RankingTopics[]>([])
//
const getTopics = async () => {
const responseData = await useKUNGalgameRankingStore().getTopics()
return responseData.data
}
//
watch(
() => topic,
async () => {
topics.value = await getTopics()
},
{ deep: true }
)
//
onMounted(async () => {
topics.value = await getTopics()
})
</script> </script>
<template> <template>
<!-- 话题排行 --> <!-- 话题排行 -->
<div class="topic-ranking"> <div class="topic">
<!-- 话题排行标题 --> <!-- 话题排行标题 -->
<div class="topic-title">最萌的话题</div> <div class="title">最萌的话题</div>
<!-- 话题排行的交互 --> <!-- 话题排行的交互 -->
<div class="topic-nav"> <div class="nav">
<!-- 浏览数 --> <!-- 升序降序 -->
<div class="view">按浏览数排序</div> <div class="order">排序</div>
<!-- 点赞数 -->
<div class="like">按点赞数排序</div> <div class="field">
<!-- 按回复数排序 --> <!-- 排序子菜单 -->
<div class="reply">按回复数排序</div> <div class="sort-submenu">
<!-- 按回复数排序 --> <div
<div class="comment">按评论数排序</div> class="sort-item"
<!-- 按推话题数排序 --> v-for="kun in topicNavSortItem"
<div class="top">按推数排序</div> :key="kun.index"
@click="topic.sortField = kun.sortField"
>
<span><Icon class="icon-item" :icon="kun.icon" /></span>
<span>按时间排序</span>
</div>
</div>
</div>
</div> </div>
<!-- 单个话题的容器 --> <!-- 单个话题的容器 -->
<div class="topic-container"> <div class="container">
<Topic /> <Topic :field="topic.sortField" :topics="topics" />
</div> </div>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
/* 话题排行 */ /* 话题排行 */
.topic-ranking { .topic {
width: 50%; width: 50%;
} }
/* 话题排行标题 */ /* 话题排行标题 */
.topic-title { .title {
font-size: 30px; font-size: 30px;
color: var(--kungalgame-blue-4); color: var(--kungalgame-blue-4);
height: 50px; height: 50px;
@ -43,56 +81,31 @@ import Topic from './Topic.vue'
margin-bottom: 20px; margin-bottom: 20px;
} }
/* 话题排行的交互 */ /* 话题排行的交互 */
.topic-nav { .nav {
display: flex; display: flex;
justify-content: space-around;
margin-left: 10px; margin-left: 10px;
cursor: pointer;
}
.topic-nav > div:hover {
background-color: var(--kungalgame-trans-blue-2);
transition: 0.2s;
}
/* 单个交互项目 */
.view,
.like,
.comment,
.reply,
.top {
height: 30px;
width: 100%;
border: 1px solid var(--kungalgame-blue-4);
display: flex;
justify-content: center;
align-items: center;
}
.like,
.reply,
.comment,
.top {
border-left: none;
} }
/* 单个话题的容器 */ /* 单个话题的容器 */
.topic-container { .container {
height: 650px; height: 100%;
border: 1px solid var(--kungalgame-blue-4);
border-top: none; border-top: none;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-left: 10px; margin-left: 10px;
overflow-y: scroll; overflow-y: scroll;
/* 兼容火狐 */
scrollbar-width: thin; &::-webkit-scrollbar {
scrollbar-color: var(--kungalgame-blue-4) var(--kungalgame-blue-1); /* Firefox 64+ */
}
/* 滚动条的样式 */
.topic-container::-webkit-scrollbar {
display: inline; display: inline;
width: 4px; width: 4px;
height: 0; height: 0;
} }
.topic-container::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
background: var(--kungalgame-blue-4); background: var(--kungalgame-blue-4);
border-radius: 2px; border-radius: 2px;
}
/* 兼容火狐 */
scrollbar-width: thin;
scrollbar-color: var(--kungalgame-blue-4) var(--kungalgame-blue-1);
} }
</style> </style>

View file

@ -0,0 +1,45 @@
interface Topic {
index: number
icon: string
name: string
sortField: string
}
export const topicNavSortItem: Topic[] = [
{
index: 1,
icon: 'bi:fire',
name: 'popularity',
sortField: 'popularity',
},
{
index: 2,
icon: 'bi:rocket',
name: 'upvote',
sortField: 'upvotes',
},
{
index: 3,
icon: 'ic:outline-remove-red-eye',
name: 'views',
sortField: 'views',
},
{
index: 4,
icon: 'line-md:thumbs-up-twotone',
name: 'likes',
sortField: 'likes',
},
{
index: 5,
icon: 'ri:reply-line',
name: 'replies',
sortField: 'replies',
},
{
index: 6,
icon: 'fa-regular:comment-dots',
name: 'comments',
sortField: 'comments',
},
]

View file

@ -37,7 +37,7 @@ const {
tags, tags,
edited, edited,
user, user,
// rid, // replies,
status, status,
share, share,
category, category,

View file

@ -4,7 +4,7 @@
这个区域包含所有人回复给楼主的话题其中每个人的话题将会被拆分成为单独的组件 这个区域包含所有人回复给楼主的话题其中每个人的话题将会被拆分成为单独的组件
--> -->
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, onUnmounted, onUpdated, ref } from 'vue' import { computed, ref } from 'vue'
import { Icon } from '@iconify/vue' import { Icon } from '@iconify/vue'
// //
import Content from '../Content.vue' import Content from '../Content.vue'