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(
requestData: Home.HomeTopicRequestData
): Promise<Home.HomeTopicResponseData> {
try {
const queryParams = objectToQueryParams(requestData)
// 调用 fetchPost 函数
const response = await fetchGet<Home.HomeTopicResponseData>(
`${homeURLs.home}?${queryParams}`
)
return response
} catch (error) {
// 处理错误
console.error(error)
throw new Error('Failed to fetch home topic')
}
}
// 首页今日热门话题
export async function getHomeNavHotTopicApi(): Promise<Home.HomeHotTopicResponseData> {
try {
// 调用 fetchPost 函数
const response = await fetchGet<Home.HomeHotTopicResponseData>(
homeURLs.navHot
)
return response
} catch (error) {
// 处理错误
console.error(error)
throw new Error('Failed to fetch home nav hot topic')
}
}
// 首页今日最新话题
export async function getHomeNavNewTopicApi(): Promise<Home.HomeNewTopicResponseData> {
try {
// 调用 fetchPost 函数
const response = await fetchGet<Home.HomeNewTopicResponseData>(
homeURLs.navNew
)
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
title: string
views: number
likes: number[]
replies: number[]
upvotesCount: number
likesCount: number
repliesCount: number
comments: number
time: number
content: string
upvotes: number[]
tags: string[]
category: string[]
popularity: number

View file

@ -8,6 +8,7 @@ export * from './edit/types/edit'
export * from './home/types/home'
export * from './user/types/user'
export * from './login/types/login'
export * from './ranking/types/ranking'
export * from './topic/types'
export * from './update-log/types/updateLog'
@ -17,5 +18,6 @@ export * from './edit'
export * from './home'
export * from './user'
export * from './login'
export * from './ranking'
export * from './topic'
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[]
edited: number
user: TopicUserInfo
rid: number[]
replies: number[]
status: number
share: number[]
category: string[]

View file

@ -12,11 +12,11 @@ export interface UserInfo {
dislike: number
daily_topic_count: number
topic: number[]
reply: number[]
comment: number[]
like_topic: number[]
upvote_topic: number[]
topicCount: number[]
replyCount: number[]
commentCount: number[]
likeTopic: 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) => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const regex = /^[^\s@]{1,64}@[^\s@]{1,255}\.[^\s@]{1,24}$/
return regex.test(email)
}

View file

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

View file

@ -1,26 +1,44 @@
<script setup lang="ts">
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>
<template>
<!-- 单个话题 -->
<div class="single-topic">
<div class="single-topic" v-for="(topic, index) in topics" :key="index">
<!-- 话题的名字 -->
<div class="topic-name">
啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星啊这可海星
{{ topic.title }}
</div>
<!-- 话题的其它信息 -->
<div class="detail">
<!-- 浏览数 -->
<span><Icon icon="ic:outline-remove-red-eye" />1007</span>
<!-- 点赞数 -->
<span><Icon icon="line-md:thumbs-up-twotone" />1007</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>
<span>
<Icon :icon="icons[props.field]" />
{{ parseTopicNumber(topic.field) }}
</span>
</div>
</div>
</template>

View file

@ -1,39 +1,77 @@
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { Icon } from '@iconify/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>
<template>
<!-- 话题排行 -->
<div class="topic-ranking">
<div class="topic">
<!-- 话题排行标题 -->
<div class="topic-title">最萌的话题</div>
<div class="title">最萌的话题</div>
<!-- 话题排行的交互 -->
<div class="topic-nav">
<!-- 浏览数 -->
<div class="view">按浏览数排序</div>
<!-- 点赞数 -->
<div class="like">按点赞数排序</div>
<!-- 按回复数排序 -->
<div class="reply">按回复数排序</div>
<!-- 按回复数排序 -->
<div class="comment">按评论数排序</div>
<!-- 按推话题数排序 -->
<div class="top">按推数排序</div>
<div class="nav">
<!-- 升序降序 -->
<div class="order">排序</div>
<div class="field">
<!-- 排序子菜单 -->
<div class="sort-submenu">
<div
class="sort-item"
v-for="kun in topicNavSortItem"
:key="kun.index"
@click="topic.sortField = kun.sortField"
>
<span><Icon class="icon-item" :icon="kun.icon" /></span>
<span>按时间排序</span>
</div>
</div>
</div>
</div>
<!-- 单个话题的容器 -->
<div class="topic-container">
<Topic />
<div class="container">
<Topic :field="topic.sortField" :topics="topics" />
</div>
</div>
</template>
<style lang="scss" scoped>
/* 话题排行 */
.topic-ranking {
.topic {
width: 50%;
}
/* 话题排行标题 */
.topic-title {
.title {
font-size: 30px;
color: var(--kungalgame-blue-4);
height: 50px;
@ -43,56 +81,31 @@ import Topic from './Topic.vue'
margin-bottom: 20px;
}
/* 话题排行的交互 */
.topic-nav {
.nav {
display: flex;
justify-content: space-around;
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 {
height: 650px;
border: 1px solid var(--kungalgame-blue-4);
.container {
height: 100%;
border-top: none;
display: flex;
flex-direction: column;
margin-left: 10px;
overflow-y: scroll;
/* 兼容火狐 */
scrollbar-width: thin;
scrollbar-color: var(--kungalgame-blue-4) var(--kungalgame-blue-1); /* Firefox 64+ */
}
/* 滚动条的样式 */
.topic-container::-webkit-scrollbar {
&::-webkit-scrollbar {
display: inline;
width: 4px;
height: 0;
}
.topic-container::-webkit-scrollbar-thumb {
&::-webkit-scrollbar-thumb {
background: var(--kungalgame-blue-4);
border-radius: 2px;
}
/* 兼容火狐 */
scrollbar-width: thin;
scrollbar-color: var(--kungalgame-blue-4) var(--kungalgame-blue-1);
}
</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,
edited,
user,
// rid,
// replies,
status,
share,
category,

View file

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