feat: add create & edit article page
This commit is contained in:
parent
f7c4dbb3d5
commit
125c23d55a
|
|
@ -26,20 +26,18 @@
|
|||
</button>
|
||||
|
||||
<AppLink
|
||||
v-if="displayEditButton"
|
||||
class="btn btn-outline-secondary btn-sm space"
|
||||
name="editor"
|
||||
name="edit-article"
|
||||
:params="{slug: article.slug}"
|
||||
>
|
||||
<i class="ion-edit space" /> Edit Article
|
||||
</AppLink>
|
||||
|
||||
<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
|
||||
v-if="displayEditButton"
|
||||
class="btn btn-outline-danger btn-sm"
|
||||
@click="onDelete"
|
||||
>
|
||||
<i class="ion-trash-a" /> Delete Article
|
||||
</button>
|
||||
|
|
@ -47,13 +45,31 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { computed, defineComponent, PropType } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { deleteArticle } from '../services/article/deleteArticle'
|
||||
import { Store } from '../store'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ArticleMeta',
|
||||
props: {
|
||||
article: { type: Object as PropType<Article>, required: true },
|
||||
},
|
||||
setup (props) {
|
||||
const router = useRouter()
|
||||
const store = useStore<Store>()
|
||||
const user = computed<Store['user']>(() => store.state.user)
|
||||
|
||||
const displayEditButton = computed(() => user.value.username === props.article.author?.username)
|
||||
|
||||
const onDelete = async () => {
|
||||
await deleteArticle(props.article.slug)
|
||||
return router.push({ name: 'global-feed' })
|
||||
}
|
||||
|
||||
return { displayEditButton, onDelete }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export function useNavigationLinks ({ user }: UseLinksProps) {
|
|||
display: 'anonym',
|
||||
},
|
||||
{
|
||||
name: 'editor',
|
||||
name: 'create-article',
|
||||
title: 'New Post',
|
||||
display: 'authorized',
|
||||
icon: 'ion-compose',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
<template>
|
||||
<div class="editor-page">
|
||||
<div class="container page">
|
||||
<div class="row">
|
||||
<div class="col-md-10 offset-md-1 col-xs-12">
|
||||
<form @submit.prevent="onSubmit">
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
v-model="form.title"
|
||||
type="text"
|
||||
class="form-control form-control-lg"
|
||||
placeholder="Article Title"
|
||||
>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
v-model="form.description"
|
||||
type="text"
|
||||
class="form-control form-control-lg"
|
||||
placeholder="What's this article about?"
|
||||
>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<textarea
|
||||
v-model="form.body"
|
||||
:rows="8"
|
||||
class="form-control"
|
||||
placeholder="Write your article (in markdown)"
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<input
|
||||
:value="form.tagList.join(' ')"
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="Enter tags"
|
||||
@input="value => (form.tagList = value.split(' '))"
|
||||
>
|
||||
<div class="tag-list" />
|
||||
</fieldset>
|
||||
<button
|
||||
class="btn btn-lg pull-xs-right btn-primary"
|
||||
type="submit"
|
||||
:disabled="!(form.title && form.description && form.body)"
|
||||
>
|
||||
Publish Article
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, reactive } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { getArticle } from '../services/article/getArticle'
|
||||
import { postArticle, putArticle } from '../services/article/postArticle'
|
||||
|
||||
interface FormState {
|
||||
title: string;
|
||||
description: string;
|
||||
body: string;
|
||||
tagList: string[];
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'EditArticle',
|
||||
setup () {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const slug = computed<string>(() => route.params.slug as string)
|
||||
|
||||
const form = reactive<FormState>({
|
||||
title: '',
|
||||
description: '',
|
||||
body: '',
|
||||
tagList: [],
|
||||
})
|
||||
|
||||
async function fetchArticle (slug: string) {
|
||||
const article = await getArticle(slug)
|
||||
|
||||
// FIXME: I always feel a little wordy here
|
||||
form.title = article.title
|
||||
form.description = article.description
|
||||
form.body = article.body
|
||||
form.tagList = article.tagList
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (slug.value) fetchArticle(slug.value)
|
||||
})
|
||||
|
||||
const onSubmit = async () => {
|
||||
let article: Article
|
||||
if (slug.value) {
|
||||
article = await putArticle(slug.value, form)
|
||||
} else {
|
||||
article = await postArticle(form)
|
||||
}
|
||||
return router.push({ name: 'article', params: { slug: article.slug } })
|
||||
}
|
||||
|
||||
return { form, onSubmit }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
@ -2,15 +2,16 @@ import { createRouter, createWebHashHistory } from 'vue-router'
|
|||
import Home from './pages/Home.vue'
|
||||
|
||||
export type AppRouteNames = 'global-feed'
|
||||
|'my-feed'
|
||||
|'tag'
|
||||
|'article'
|
||||
|'login'
|
||||
|'register'
|
||||
|'profile'
|
||||
|'profile-favorites'
|
||||
|'editor'
|
||||
|'settings'
|
||||
| 'my-feed'
|
||||
| 'tag'
|
||||
| 'article'
|
||||
| 'create-article'
|
||||
| 'edit-article'
|
||||
| 'login'
|
||||
| 'register'
|
||||
| 'profile'
|
||||
| 'profile-favorites'
|
||||
| 'settings'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
|
|
@ -35,6 +36,16 @@ const router = createRouter({
|
|||
path: '/article/:slug',
|
||||
component: () => import('./pages/Article.vue'),
|
||||
},
|
||||
{
|
||||
name: 'edit-article',
|
||||
path: '/article/:slug/edit',
|
||||
component: () => import('./pages/EditArticle.vue'),
|
||||
},
|
||||
{
|
||||
name: 'create-article',
|
||||
path: '/article/create',
|
||||
component: () => import('./pages/EditArticle.vue'),
|
||||
},
|
||||
{
|
||||
name: 'login',
|
||||
path: '/login',
|
||||
|
|
@ -55,11 +66,6 @@ const router = createRouter({
|
|||
path: '/profile/:username/favorites',
|
||||
component: () => import('./pages/Profile.vue'),
|
||||
},
|
||||
{
|
||||
name: 'editor',
|
||||
path: '/editor',
|
||||
redirect: '/',
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
path: '/settings',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import { request } from '../index'
|
||||
|
||||
export async function deleteArticle (slug: string): Promise<void> {
|
||||
return request.delete(`/articles/${slug}`)
|
||||
}
|
||||
Loading…
Reference in New Issue