feat : Home Article ReRender Success and Pagination Template add
This commit is contained in:
@@ -1,34 +1,62 @@
|
|||||||
import { ref, Ref } from "@vue/reactivity";
|
import { ref, Ref } from "@vue/reactivity";
|
||||||
import { listArticles } from "@/api/index";
|
import { listArticles, feedArticle } from "@/api/index";
|
||||||
|
|
||||||
import { usePagination } from "@/ts/usePagination";
|
import { usePagination } from "@/ts/usePagination";
|
||||||
|
|
||||||
|
|
||||||
export interface Todo {
|
export interface ArticleList {
|
||||||
id: number;
|
slug: string,
|
||||||
title: string;
|
title: string,
|
||||||
|
description: string,
|
||||||
|
favorited: boolean,
|
||||||
|
favoritesCount: number,
|
||||||
|
createdAt: string,
|
||||||
|
author: {
|
||||||
|
username: string,
|
||||||
|
image: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePaginationApi(
|
export function usePaginationApi(
|
||||||
currentPage: Ref<number>,
|
currentPage: Ref<number>,
|
||||||
rowsPerPage?: Ref<number>
|
rowsPerPage?: Ref<number>
|
||||||
) {
|
) {
|
||||||
const articleLists: Ref<Todo[]> = ref([]);
|
const articleLists: Ref<ArticleList[]> = ref([]);
|
||||||
|
|
||||||
const listsAreLoading = ref(false);
|
const listsAreLoading = ref(false);
|
||||||
|
const isEmpty = ref(false);
|
||||||
|
|
||||||
const { paginatedArray, numberOfPages } = usePagination<Todo>({
|
const { paginatedArray, numberOfPages } = usePagination<ArticleList>({
|
||||||
rowsPerPage,
|
rowsPerPage,
|
||||||
arrayToPaginate: articleLists,
|
arrayToPaginate: articleLists,
|
||||||
currentPage
|
currentPage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const feedLists = async () => {
|
||||||
|
listsAreLoading.value = true;
|
||||||
|
isEmpty.value = false;
|
||||||
|
try{
|
||||||
|
const { data } = await feedArticle();
|
||||||
|
articleLists.value = data.articles;
|
||||||
|
if(data.articlesCount == 0){
|
||||||
|
isEmpty.value = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
} finally {
|
||||||
|
listsAreLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const loadLists = async () => {
|
const loadLists = async () => {
|
||||||
listsAreLoading.value = true;
|
listsAreLoading.value = true;
|
||||||
|
isEmpty.value = false;
|
||||||
try {
|
try {
|
||||||
const { data } = await listArticles();
|
const { data } = await listArticles();
|
||||||
articleLists.value = data.articles;
|
articleLists.value = data.articles;
|
||||||
console.log(articleLists.value);
|
if(data.articlesCount == 0){
|
||||||
|
isEmpty.value = true;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -39,7 +67,9 @@ export function usePaginationApi(
|
|||||||
return {
|
return {
|
||||||
articleLists: paginatedArray,
|
articleLists: paginatedArray,
|
||||||
loadLists,
|
loadLists,
|
||||||
|
feedLists,
|
||||||
listsAreLoading,
|
listsAreLoading,
|
||||||
|
isEmpty,
|
||||||
numberOfPages
|
numberOfPages
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,45 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-for = "art in articles.article">
|
|
||||||
<div class="article-preview">
|
<div class="article-preview">
|
||||||
<div class="article-meta">
|
<div class="article-meta">
|
||||||
<a href="profile.html"><img :src="art.author.image"/></a>
|
<a href="javascript:(0)" @click="showProfile(article.author.username)"><img :src="article.author.image"/></a>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<a href="" class="author">{{art.author.username}}</a>
|
<a class="author"
|
||||||
<span class="date">{{convertDate(art.createdAt)}}</span>
|
href="javascript:void(0)"
|
||||||
|
@click="showProfile(article.author.username)">{{article.author.username}}</a>
|
||||||
|
<span class="date">{{convertDate(article.createdAt)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-outline-primary btn-sm pull-xs-right">
|
<button class="btn btn-outline-primary btn-sm pull-xs-right">
|
||||||
<i class="ion-heart"></i> {{art.favoritesCount}}
|
<i class="ion-heart" @click="changeFavorite(article.slug, article.favorited)"></i> {{article.favoritesCount}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<a href="" class="preview-link">
|
<a href="javascript:(0)"
|
||||||
<h1>{{art.title}}</h1>
|
class="preview-link"
|
||||||
<p>{{art.description}}</p>
|
@click="showArticle(article.slug)">
|
||||||
|
<h1>{{article.title}}</h1>
|
||||||
|
<p>{{article.description}}</p>
|
||||||
<span>Read more...</span>
|
<span>Read more...</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMounted, reactive, ref, defineComponent} from "vue";
|
import { defineComponent } from "vue";
|
||||||
import {useStore} from "vuex";
|
|
||||||
import axios from "axios";
|
|
||||||
import convertDate from "@/ts/common";
|
import convertDate from "@/ts/common";
|
||||||
import {feedArticle} from "@/api";
|
import { favoriteArticle, unFavoriteArticle } from "@/api";
|
||||||
|
import router from "@/router";
|
||||||
|
import {useStore} from "vuex";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ArticleListFeed",
|
name: "ArticleListFeed",
|
||||||
props:{
|
props: {
|
||||||
isEmpty: Boolean,
|
article: {
|
||||||
isLoading: Boolean
|
type: Object,
|
||||||
},
|
default: () => {
|
||||||
setup(props,{ emit }){
|
return {
|
||||||
const articles = reactive({
|
|
||||||
article:[
|
|
||||||
{
|
|
||||||
slug: "",
|
slug: "",
|
||||||
title: "",
|
title: "",
|
||||||
description: "",
|
description: "",
|
||||||
|
favorited: false,
|
||||||
favoritesCount: 0,
|
favoritesCount: 0,
|
||||||
createdAt: "",
|
createdAt: "",
|
||||||
author: {
|
author: {
|
||||||
@@ -47,24 +48,45 @@ export default defineComponent({
|
|||||||
image: ""
|
image: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
}
|
||||||
articlesCount: ""}
|
}
|
||||||
)
|
},
|
||||||
|
setup(props){
|
||||||
|
const store = useStore();
|
||||||
|
const token = store.state.token;
|
||||||
|
|
||||||
onMounted(async () => {
|
const showProfile = (username: string) => {
|
||||||
try {
|
router.push({
|
||||||
const { data } = await feedArticle();
|
name: 'Profile',
|
||||||
articles.article = data.articles.slice();
|
params: {username: username}
|
||||||
articles.articlesCount = data.articlesCount;
|
|
||||||
emit("loading",false);
|
|
||||||
if(parseInt(articles.articlesCount) == 0) {
|
|
||||||
emit("emptied",true);
|
|
||||||
}
|
|
||||||
}catch (error: any){
|
|
||||||
alert(error);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return { articles, convertDate }
|
}
|
||||||
|
|
||||||
|
const showArticle = (slug: string) =>{
|
||||||
|
router.push({
|
||||||
|
name: 'ArticleDetail',
|
||||||
|
params: {slug: slug}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeFavorite = async (slug: string, favorite : boolean) => {
|
||||||
|
if(token == ''){
|
||||||
|
await router.push({name:"Login"});
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
if(favorite){
|
||||||
|
const { data } = await unFavoriteArticle(slug);
|
||||||
|
props.article.favoritesCount = data.article.favoritesCount;
|
||||||
|
props.article.favorited = data.article.favorited;
|
||||||
|
}else{
|
||||||
|
const { data } = await favoriteArticle(slug);
|
||||||
|
props.article.favoritesCount = data.article.favoritesCount;
|
||||||
|
props.article.favorited = data.article.favorited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { convertDate, changeFavorite, showProfile, showArticle }
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,46 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-for = "art in articles.article">
|
|
||||||
<div class="article-preview">
|
<div class="article-preview">
|
||||||
<div class="article-meta">
|
<div class="article-meta">
|
||||||
<a href="javascript:(0)" @click="showProfile(art.author.username)"><img :src="art.author.image"/></a>
|
<a href="javascript:(0)" @click="showProfile(article.author.username)"><img :src="article.author.image"/></a>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<a class="author"
|
<a class="author"
|
||||||
href="javascript:void(0)"
|
href="javascript:void(0)"
|
||||||
@click="showProfile(art.author.username)">{{art.author.username}}</a>
|
@click="showProfile(article.author.username)">{{article.author.username}}</a>
|
||||||
<span class="date">{{convertDate(art.createdAt)}}</span>
|
<span class="date">{{convertDate(article.createdAt)}}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-outline-primary btn-sm pull-xs-right">
|
<button class="btn btn-outline-primary btn-sm pull-xs-right">
|
||||||
<i class="ion-heart" @click="changeFavorite(art.slug, art.favorited)"></i> {{art.favoritesCount}}
|
<i class="ion-heart" @click="changeFavorite(article.slug, article.favorited)"></i> {{article.favoritesCount}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<a href="javascript:(0)"
|
<a href="javascript:(0)"
|
||||||
class="preview-link"
|
class="preview-link"
|
||||||
@click="showArticle(art.slug)">
|
@click="showArticle(article.slug)">
|
||||||
<h1>{{art.title}}</h1>
|
<h1>{{article.title}}</h1>
|
||||||
<p>{{art.description}}</p>
|
<p>{{article.description}}</p>
|
||||||
<span>Read more...</span>
|
<span>Read more...</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMounted, reactive, defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import convertDate from '@/ts/common';
|
import convertDate from '@/ts/common';
|
||||||
import {favoriteArticle, listArticles, unFavoriteArticle} from "@/api";
|
import { useStore } from "vuex";
|
||||||
|
import { favoriteArticle, unFavoriteArticle } from "@/api";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ArticleListGlobal",
|
name: "ArticleListGlobal",
|
||||||
props:{
|
props:{
|
||||||
isLoading: Boolean,
|
index: Number,
|
||||||
isEmpty: Boolean,
|
article: {
|
||||||
globalActive: Boolean,
|
type: Object,
|
||||||
},
|
default: () => {
|
||||||
setup(props,{emit}) {
|
return {
|
||||||
const articles = reactive({
|
|
||||||
article:[
|
|
||||||
{
|
|
||||||
slug: "",
|
slug: "",
|
||||||
title: "",
|
title: "",
|
||||||
description: "",
|
description: "",
|
||||||
@@ -52,9 +48,12 @@ export default defineComponent({
|
|||||||
image: ""
|
image: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
}
|
||||||
articlesCount: ""}
|
}
|
||||||
)
|
},
|
||||||
|
setup(props) {
|
||||||
|
const store = useStore();
|
||||||
|
const token = store.state.token;
|
||||||
|
|
||||||
const showProfile = (username: string) => {
|
const showProfile = (username: string) => {
|
||||||
router.push({
|
router.push({
|
||||||
@@ -71,29 +70,23 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const changeFavorite = async (slug: string, favorite : boolean) => {
|
const changeFavorite = async (slug: string, favorite : boolean) => {
|
||||||
if(favorite){
|
if(token == ''){
|
||||||
await unFavoriteArticle(slug);
|
await router.push({name:"Login"});
|
||||||
|
return;
|
||||||
}else{
|
}else{
|
||||||
await favoriteArticle(slug);
|
if(favorite){
|
||||||
|
const { data } = await unFavoriteArticle(slug);
|
||||||
|
props.article.favoritesCount = data.article.favoritesCount;
|
||||||
|
props.article.favorited = data.article.favorited;
|
||||||
|
}else{
|
||||||
|
const { data } = await favoriteArticle(slug);
|
||||||
|
props.article.favoritesCount = data.article.favoritesCount;
|
||||||
|
props.article.favorited = data.article.favorited;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { convertDate, changeFavorite, showProfile, showArticle }
|
||||||
onMounted(async () => {
|
|
||||||
console.log(props.isEmpty,props.globalActive,props.isLoading);
|
|
||||||
try {
|
|
||||||
const { data } = await listArticles();
|
|
||||||
articles.article = data.articles.slice();
|
|
||||||
articles.articlesCount = data.articlesCount;
|
|
||||||
emit("loading",false);
|
|
||||||
if(parseInt(articles.articlesCount) == 0) {
|
|
||||||
emit("emptied",true);
|
|
||||||
}
|
|
||||||
}catch (error: any){
|
|
||||||
alert(error);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return { articles, convertDate, changeFavorite, showProfile, showArticle }
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,38 +26,34 @@
|
|||||||
:class="{ active : globalActive }">Global Feed</a>
|
:class="{ active : globalActive }">Global Feed</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div v-if="isLoading">
|
<div v-if="listsAreLoading">
|
||||||
Loading articles...
|
Loading articles...
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isEmpty">
|
<div v-if="isEmpty">
|
||||||
No articles are here... yet.
|
No articles are here... yet.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<article-list
|
<div v-if="feedActive && isLogin">
|
||||||
v-if="isLogin && feedActive"
|
<article-list-feed v-for="(article,index) in articleLists"
|
||||||
:value="isLogin"
|
:key="article.slug"
|
||||||
:value2="isEmpty"
|
:article="article">
|
||||||
@loading="onChangeLoading"
|
</article-list-feed>
|
||||||
@emptied="emptyCheck">
|
</div>
|
||||||
</article-list>
|
<div v-else>
|
||||||
<article-list-global
|
<article-list-global v-for="(article,index) in articleLists"
|
||||||
v-else
|
:key="article.slug"
|
||||||
:isLoading="isLoading"
|
:index="index"
|
||||||
:isEmpty="isEmpty"
|
@update:Favorite="updateFavorite"
|
||||||
:globalActive="globalActive"
|
:article="article">
|
||||||
@loading="onChangeLoading"
|
|
||||||
@emptied="emptyCheck">
|
|
||||||
</article-list-global>
|
</article-list-global>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<p>Popular Tags</p>
|
<p>Popular Tags</p>
|
||||||
<tag-lists></tag-lists>
|
<tag-lists></tag-lists>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="test in articleLists">
|
|
||||||
{{test}}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -78,48 +74,49 @@ import { useStore } from "vuex";
|
|||||||
export default {
|
export default {
|
||||||
name: "TheHome",
|
name: "TheHome",
|
||||||
components: {
|
components: {
|
||||||
'article-list': articleList,
|
'article-list-feed': articleList,
|
||||||
'article-list-global': articleListGlobal,
|
'article-list-global': articleListGlobal,
|
||||||
'tag-lists': tagLists,
|
'tag-lists': tagLists,
|
||||||
'pagination-component': pagination,
|
'pagination-component': pagination,
|
||||||
},
|
},
|
||||||
setup(){
|
setup(){
|
||||||
const isLoading = ref(true);
|
|
||||||
const isEmpty = ref(false);
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const isLogin = store.state.token == '' ? false : true;
|
const isLogin = ref(false);
|
||||||
const feedActive = ref(true);
|
const feedActive = ref(true);
|
||||||
const globalActive = ref(false);
|
const globalActive = ref(false);
|
||||||
|
|
||||||
const currentPage = ref(1);
|
const currentPage = ref(1);
|
||||||
const rowsPerPage = ref(20);
|
const rowsPerPage = ref(20);
|
||||||
|
|
||||||
const { articleLists, listsAreLoading, loadLists, numberOfPages } = usePaginationApi(currentPage, rowsPerPage);
|
const { articleLists, listsAreLoading, isEmpty, loadLists, feedLists, numberOfPages } = usePaginationApi(currentPage, rowsPerPage);
|
||||||
|
|
||||||
const onChangeLoading = (val : boolean) => {
|
const feedSelect = async () => {
|
||||||
isLoading.value = val;
|
|
||||||
}
|
|
||||||
const emptyCheck = (val: boolean) => {
|
|
||||||
isEmpty.value = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
const feedSelect = () => {
|
|
||||||
feedActive.value=true;
|
feedActive.value=true;
|
||||||
globalActive.value=false;
|
globalActive.value=false;
|
||||||
isEmpty.value=false;
|
await feedLists();
|
||||||
isLoading.value=true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalSelect = () => {
|
const globalSelect = async () => {
|
||||||
feedActive.value=false;
|
feedActive.value=false;
|
||||||
globalActive.value=true;
|
globalActive.value=true;
|
||||||
isEmpty.value=false;
|
await loadLists();
|
||||||
isLoading.value=true;
|
}
|
||||||
|
const updateFavorite = async (index: number) => {
|
||||||
|
console.log("index" , index);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => loadLists())
|
onMounted(async () => {
|
||||||
|
isLogin.value = store.state.token ? true : false;
|
||||||
|
if(isLogin.value == false) {
|
||||||
|
await loadLists();
|
||||||
|
globalActive.value = true;
|
||||||
|
}else{
|
||||||
|
await feedLists();
|
||||||
|
feedActive.value = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return { isLoading, isEmpty, isLogin, currentPage, rowsPerPage, numberOfPages, feedActive, globalActive, articleLists, onChangeLoading, emptyCheck, feedSelect, globalSelect };
|
return { listsAreLoading, isEmpty, isLogin, currentPage, rowsPerPage, numberOfPages, feedActive, globalActive, articleLists, updateFavorite, feedSelect, globalSelect };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user