Skip to content

Commit

Permalink
feat: follow feature
Browse files Browse the repository at this point in the history
  • Loading branch information
laoriy committed Dec 25, 2023
1 parent 8ed2ee7 commit 71283c8
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 36 deletions.
3 changes: 2 additions & 1 deletion 3.vue/5.server-render/nuxt-realworld/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
- [api](https://realworld-docs.netlify.app/docs/specs/frontend-specs/swagger)


# laor / 268***@qq.com qwer1234
# laor / 268***@qq.com qwer1234

84 changes: 84 additions & 0 deletions 3.vue/5.server-render/nuxt-realworld/components/ArticleList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<div
v-for="article in articles"
:key="article.slug"
class="article-preview"
>
<div class="article-meta">
<NuxtLink :to="`/profile/${article.author.username}`">
<img :src="article.author.image"
/></NuxtLink>
<div class="info">
<NuxtLink
:to="`/profile/${article.author.username}`"
class="author"
>{{ article.author.username }}</NuxtLink
>
<span class="date">{{
dateFormat(article.createdAt, "MMMM D, YYYY")
}}</span>
</div>
<button
class="btn btn-outline-primary btn-sm pull-xs-right"
:class="{ active: article.favorited }"
@click="handleFavoriteArticle(article)"
>
<i class="ion-heart"></i>
{{ article.favoritesCount }}
</button>
</div>
<NuxtLink :to="`/article/${article.slug}`" class="preview-link">
<h1>{{ article.title }}</h1>
<p>{{ article.description }}</p>
<span>Read more...</span>
<ul v-for="tag in article.tagList" :key="tag" class="tag-list">
<li class="tag-default tag-pill tag-outline">
{{ tag }}
</li>
</ul>
</NuxtLink>
</div>
<ul class="pagination">
<li
v-for="p in total"
:key="p"
:class="{ active: p === page }"
class="page-item"
>
<a
class="page-link"
href="javascript:void(0)"
@click="handlePaginationChange(p)"
>{{ p }}</a
>
</li>
</ul>
</template>

<script setup lang="ts">
import useFavorite from "~/hooks/useFavorite"
import type { Article } from "~/service/article"
const { handleFavoriteArticle } = useFavorite()
defineProps({
articles: {
type: Array as PropType<Article[]>,
default: () => [],
},
total: {
type: Number,
default: 0,
},
page: {
type: Number,
default: 1,
},
})
const emits = defineEmits(["pagination-change"])
const handlePaginationChange = (p: number) => {
emits("pagination-change", p)
}
</script>

<style scoped></style>
39 changes: 34 additions & 5 deletions 3.vue/5.server-render/nuxt-realworld/hooks/useArticles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import type {
FetchArticleParams,
BaseListParams,
} from "~/service/article"
import { getAllTags } from '~/service/tag'
import { getAllTags } from "~/service/tag"

const enum FeedTab {
Global = "global",
MyFeed = "my_feed",
Tag = "tag",
}

const enum UserArticleTab {
MY_ARTICLES = "MY_ARTICLES",
FAVORITED_ARTICLES = "FAVORITED_ARTICLES",
}

const useArticles = () => {
const { userInfo } = userStore()

Expand All @@ -20,8 +25,12 @@ const useArticles = () => {
const tags = ref([] as string[])
const currentTag = ref("")
const feedTab = ref(FeedTab.Global)
const userArticleTab = ref(UserArticleTab.MY_ARTICLES)
// for execatly user
const author = ref("")
const favorited = ref("")

const limit = ref(10)
const limit = ref(5)
const page = ref(1)
const paginationCount = computed(() =>
Math.ceil(articlesCount.value / limit.value)
Expand All @@ -36,12 +45,16 @@ const useArticles = () => {

if (feedTab.value !== FeedTab.MyFeed) Object.assign(query, { ...p })

if (author.value) Object.assign(query, { author: author.value })
if (favorited.value)
Object.assign(query, { favorited: favorited.value })

const { data } =
feedTab.value !== FeedTab.MyFeed
? await getGlobalArticles(query)
: await getFeedArticles(query)
articles.value = data.value.articles
articlesCount.value = data.value.articlesCount
articles.value = data.value.articles || []
articlesCount.value = data.value.articlesCount || 0
}
/**获取所有标签 */
const getTags = async () => {
Expand Down Expand Up @@ -76,6 +89,19 @@ const useArticles = () => {
getArticles({ author: userInfo.username })
}

const getArticlesByUsername = (username: string) => {
author.value = username
favorited.value = ""
userArticleTab.value = UserArticleTab.MY_ARTICLES
getArticles()
}
const getFavoritedArticlesByUsername = (username: string) => {
favorited.value = username
author.value = ""
userArticleTab.value = UserArticleTab.FAVORITED_ARTICLES
getArticles()
}

return {
articles,
tags,
Expand All @@ -84,14 +110,17 @@ const useArticles = () => {
paginationCount,
currentTag,
feedTab,
userArticleTab,
getArticles,
getTags,
handleTag,
handleGlobalFeed,
handleMyFeed,
handlePaginationChange,
getArticlesByUsername,
getFavoritedArticlesByUsername,
}
}

export { FeedTab }
export { FeedTab, UserArticleTab }
export default useArticles
16 changes: 16 additions & 0 deletions 3.vue/5.server-render/nuxt-realworld/hooks/useFollow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { followAnUser, unFlowAnUser, type Profile } from "~/service/profile"

function useFollow() {
const handleFollowAnUser = async (profile: Profile) => {
const { following, username } = profile
if (following) {
await unFlowAnUser(username!)
} else {
await followAnUser(username!)
}
profile.following = !profile.following
}
return { handleFollowAnUser }
}

export default useFollow
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@
<button
class="btn btn-sm btn-outline-secondary"
:class="{ active: article.author?.following }"
@click="handleFollowAnUser(article.author!)"
>
<i class="ion-plus-round"></i>
&nbsp; Follow {{ article.author?.username }}
<i class="ion-plus-round" v-if="!article.author?.following"></i>
&nbsp; {{ article.author?.following ? "Unfollow" : "Follow" }} {{ article.author?.username }}
<!-- <span class="counter">({{ article.favoritesCount }})</span> -->
</button>
&nbsp;&nbsp;
Expand All @@ -48,6 +49,7 @@
<script setup lang="ts">
import type { PropType } from "vue"
import useFavorite from "~/hooks/useFavorite"
import useFollow from "~/hooks/useFollow"
import { type Article, type PartialArticle } from "~/service/article"
defineProps({
Expand All @@ -59,6 +61,7 @@ defineProps({
const { userInfo } = userStore()
const { handleFavoriteArticle } = useFavorite()
const { handleFollowAnUser, handleUnFollowAnUser } = useFollow()
</script>

<style scoped></style>
11 changes: 5 additions & 6 deletions 3.vue/5.server-render/nuxt-realworld/pages/home/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
</li>
</ul>
</div>
<div
<!-- <div
v-for="article in articles"
:key="article.slug"
class="article-preview"
Expand Down Expand Up @@ -94,8 +94,9 @@
</li>
</ul>
</NuxtLink>
</div>
<ul class="pagination">
</div> -->
<ArticleList :articles="articles" :total="paginationCount" :page="page" @pagination-change="handlePaginationChange" />
<!-- <ul class="pagination">
<li
v-for="p in paginationCount"
:key="p"
Expand All @@ -109,7 +110,7 @@
>{{ p }}</a
>
</li>
</ul>
</ul> -->
</div>
<div class="col-md-3">
<div class="sidebar">
Expand All @@ -134,7 +135,6 @@

<script setup lang="ts">
import useArticles from "~/hooks/useArticles"
import useFavorite from "~/hooks/useFavorite"
import { FeedTab } from "~/hooks/useArticles"
const { token } = userStore()
const {
Expand All @@ -152,7 +152,6 @@ const {
handlePaginationChange,
} = useArticles()
const { handleFavoriteArticle } = useFavorite()
await Promise.all([getArticles(), getTags()]) // 这里必须加await 否则会保持 Hydration node mismatch. 因为服务端没拿到数据时生成的DOM和最终客户端要显示的模板肯定不一样DOM
</script>

Expand Down
Loading

0 comments on commit 71283c8

Please sign in to comment.