feat: add article's page

This commit is contained in:
Sándor Levcsák 2020-10-07 01:56:28 +03:00
parent 7c9157384e
commit e8fa391b26
5 changed files with 219 additions and 0 deletions

View File

@ -11,11 +11,15 @@
"test": "yarn tsc && yarn lint && yarn test:unit && yarn test:e2e"
},
"dependencies": {
"dompurify": "^2.1.1",
"marked": "^1.2.0",
"vue": "^3.0.0",
"vue-router": "^4.0.0-beta.12"
},
"devDependencies": {
"@types/dompurify": "^2.0.4",
"@types/jest": "^26.0.14",
"@types/marked": "^1.1.0",
"@typescript-eslint/eslint-plugin": "^4.3.0",
"@typescript-eslint/parser": "^4.2.0",
"@vue/compiler-sfc": "^3.0.0-rc.1",

View File

@ -0,0 +1,48 @@
<template>
<div class="card">
<div class="card-block">
<p class="card-text">
{{ comment.body }}
</p>
</div>
<div class="card-footer">
<RouterLink
:to="`/profile/${comment.author.username}`"
class="comment-author"
>
<img
:src="comment.author.image"
class="comment-author-img"
>
</RouterLink>
&nbsp;
<RouterLink
:to="`/profile/${comment.author.username}`"
class="comment-author"
>
{{ comment.author.username }}
</RouterLink>
<span class="date-posted">{{ (new Date(comment.createdAt)).toLocaleDateString() }}</span>
<span class="mod-options">
<i class="ion-edit" />
<i class="ion-trash-a" />
</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue'
export default defineComponent({
name: 'ArticleMeta',
props: {
comment: { type: Object as PropType<ArticleComment>, required: true },
},
})
</script>

View File

@ -0,0 +1,64 @@
<template>
<div class="article-meta">
<a href=""><img :src="article.author?.image"></a>
<div class="info">
<RouterLink
:to="`/profile/${article.author?.username}`"
class="author"
>
{{ article.author?.username }}
</RouterLink>
<span class="date">{{ (new Date(article.createdAt)).toLocaleDateString() }}</span>
</div>
<button class="btn btn-sm btn-outline-secondary">
<i class="ion-plus-round" />
&nbsp;
Follow {{ article.author?.username }}
</button>
&nbsp;&nbsp;
<button class="btn btn-sm btn-outline-primary">
<i class="ion-heart" />
&nbsp;
{{ article.favorited ? "Unfavorite" : "Favorite" }} Article
<span class="counter">({{ article.favoritesCount }})</span>
</button>
&nbsp;&nbsp;
<RouterLink
class="btn btn-outline-secondary btn-sm"
:to="`/editor/${article.slug}`"
>
<i class="ion-edit" /> Edit Article
</RouterLink>
&nbsp;&nbsp;
<button class="btn btn-outline-danger btn-sm">
<i class="ion-trash-a" /> Delete Article
</button>
//
<button
class="btn btn-outline-danger btn-sm disabled"
disabled
>
<i class="ion-trash-a" /> Delete Article
</button>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue'
export default defineComponent({
name: 'ArticleMeta',
props: {
article: { type: Object as PropType<Article>, required: true },
},
})
</script>

102
src/pages/Article.vue Normal file
View File

@ -0,0 +1,102 @@
<template>
<div class="article-page">
<div class="banner">
<div class="container">
<h1>{{ article.title }}</h1>
<ArticleMeta :article="article" />
</div>
</div>
<div class="container page">
<div class="row article-content">
<!-- eslint-disable vue/no-v-html -->
<div
class="col-md-12"
v-html="articleHandledBody"
/>
<!-- eslint-enable vue/no-v-html -->
</div>
<hr>
<div class="article-actions">
<ArticleMeta :article="article" />
</div>
<div class="row">
<div class="col-xs-12 col-md-8 offset-md-2">
<form class="card comment-form">
<div class="card-block">
<textarea
class="form-control"
placeholder="Write a comment..."
rows="3"
/>
</div>
<div class="card-footer">
<img
:src="article.author?.image"
class="comment-author-img"
>
<button class="btn btn-sm btn-primary">
Post Comment
</button>
</div>
</form>
<ArticleComment
v-for="comment in comments"
:key="comment.id"
:comment="comment"
/>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, reactive, ref, watchEffect } from 'vue'
import { useRoute } from 'vue-router'
import md2html from 'marked'
import DOMPurify from 'dompurify'
import { getArticle } from '../services/article/getArticle'
import { getCommentsByArticle } from '../services'
import ArticleMeta from '../components/ArticleMeta.vue'
import ArticleComment from '../components/ArticleComment.vue'
export default defineComponent({
name: 'Article',
components: {
ArticleMeta,
ArticleComment,
},
setup () {
const route = useRoute()
const slug = route.params.slug as string
const article = reactive<Article>({} as Article)
const comments = ref<ArticleComment[]>([])
watchEffect(async () => {
const articleData = await getArticle(slug)
Object.assign(article, articleData)
const commentsData = await getCommentsByArticle(slug)
comments.value = commentsData
})
const articleHandledBody = computed(
() => !article.body ? '' : md2html(article.body, { sanitizer: DOMPurify.sanitize }),
)
return {
article,
articleHandledBody,
comments,
}
},
})
</script>

View File

@ -7,6 +7,7 @@ const router = createRouter({
{ path: '/', component: Home },
{ path: '/my-feeds', component: Home },
{ path: '/tag/:tag', component: Home },
{ path: '/article/:slug', component: () => import('./pages/Article.vue') },
],
})