Article editor

This commit is contained in:
LittleSheep 2024-03-09 17:32:45 +08:00
parent fa5b166d88
commit 1f69fcbfb4
5 changed files with 169 additions and 10 deletions

View File

@ -0,0 +1,152 @@
<template>
<v-card :rounded="false">
<v-form @submit.prevent="postArticle">
<v-toolbar>
<div class="article-toolbar">
<div class="flex">
<v-btn type="button" icon="mdi-close" @click="editor.show.article = false"></v-btn>
</div>
<v-toolbar-title>Write an article</v-toolbar-title>
<div class="flex justify-end items-center">
<v-tooltip text="Publish">
<template #activator="{ props: binding }">
<v-btn type="submit" icon="mdi-publish" v-bind="binding" :loading="loading" />
</template>
</v-tooltip>
</div>
</div>
</v-toolbar>
<v-card-text>
<v-container class="article-container">
<v-textarea
required
class="mb-3"
variant="outlined"
label="Content"
hint="The content supports markdown syntax"
v-model="data.content"
/>
<v-expansion-panels>
<v-expansion-panel title="Brief describe">
<template #text>
<div class="mt-1">
<v-text-field
required
variant="solo-filled"
density="comfortable"
label="Title"
v-model="data.title"
/>
<v-textarea
required
auto-grow
variant="solo-filled"
density="comfortable"
label="Description"
v-model="data.description"
/>
</div>
</template>
</v-expansion-panel>
<v-expansion-panel title="Planned publish">
<template #text>
<div class="flex justify-between items-center">
<div>
<p class="text-xs">Your content will visible for public at</p>
<p class="text-lg font-medium">
{{ data.publishedAt ? new Date(data.publishedAt).toLocaleString() : new Date().toLocaleString() }}
</p>
</div>
<v-btn size="small" icon="mdi-pencil" variant="text" @click="dialogs.plan = true" />
</div>
</template>
</v-expansion-panel>
</v-expansion-panels>
</v-container>
</v-card-text>
</v-form>
</v-card>
<planned-publish v-model:show="dialogs.plan" v-model:value="data.publishedAt" />
<v-snackbar v-model="success" :timeout="3000">Your article has been published.</v-snackbar>
<!-- @vue-ignore -->
<v-snackbar v-model="error" :timeout="5000">Something went wrong... {{ error }}</v-snackbar>
</template>
<script setup lang="ts">
import { request } from "@/scripts/request"
import { useEditor } from "@/stores/editor"
import { getAtk } from "@/stores/userinfo"
import { reactive, ref } from "vue"
import PlannedPublish from "@/components/publish/parts/PlannedPublish.vue"
import { useRouter } from "vue-router"
const editor = useEditor()
const dialogs = reactive({
plan: false,
categories: false,
media: false
})
const data = reactive<any>({
title: "",
content: "",
description: "",
publishedAt: null
})
const router = useRouter()
const error = ref<string | null>(null)
const success = ref(false)
const loading = ref(false)
async function postArticle(evt: SubmitEvent) {
const form = evt.target as HTMLFormElement
if (!data.content) return
if (!data.title || !data.description) return
if (!data.publishedAt) data.publishedAt = new Date().toISOString()
loading.value = true
const res = await request("/api/p/articles", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAtk()}` },
body: JSON.stringify(data)
})
if (res.status === 200) {
const data = await res.json()
form.reset()
router.push({ name: "posts.details", params: { postType: "articles", alias: data.alias } })
success.value = true
editor.show.article = false
} else {
error.value = await res.text()
}
loading.value = false
}
</script>
<style scoped>
.article-toolbar {
display: grid;
flex-grow: 1;
align-items: center;
margin: 0 16px;
grid-template-columns: 1fr auto 1fr;
}
.article-container {
max-width: 720px;
}
</style>

View File

@ -44,7 +44,8 @@ const success = ref(false)
const loading = ref(false) const loading = ref(false)
async function postComment(evt: SubmitEvent) { async function postComment(evt: SubmitEvent) {
const data = new FormData(evt.target as HTMLFormElement) const form = evt.target as HTMLFormElement
const data = new FormData(form)
if (!data.has("content")) return if (!data.has("content")) return
loading.value = true loading.value = true
@ -54,12 +55,13 @@ async function postComment(evt: SubmitEvent) {
body: data body: data
}) })
if (res.status === 200) { if (res.status === 200) {
form.reset()
success.value = true success.value = true
editor.show.comment = false
} else { } else {
error.value = await res.text() error.value = await res.text()
} }
loading.value = false loading.value = false
editor.show.comment = false
editor.done = true editor.done = true
} }
</script> </script>

View File

@ -85,7 +85,8 @@ const success = ref(false)
const loading = ref(false) const loading = ref(false)
async function postMoment(evt: SubmitEvent) { async function postMoment(evt: SubmitEvent) {
const data = new FormData(evt.target as HTMLFormElement) const form = evt.target as HTMLFormElement
const data = new FormData(form)
if (!data.has("content")) return if (!data.has("content")) return
if (!extras.publishedAt) data.set("published_at", new Date().toISOString()) if (!extras.publishedAt) data.set("published_at", new Date().toISOString())
else data.set("published_at", extras.publishedAt) else data.set("published_at", extras.publishedAt)
@ -97,11 +98,12 @@ async function postMoment(evt: SubmitEvent) {
body: data body: data
}) })
if (res.status === 200) { if (res.status === 200) {
form.reset()
success.value = true success.value = true
editor.show.moment = false
} else { } else {
error.value = await res.text() error.value = await res.text()
} }
loading.value = false loading.value = false
editor.show.moment = false
} }
</script> </script>

View File

@ -1,9 +1,12 @@
<template> <template>
<v-dialog v-model="editor.show.moment" class="max-w-[540px]"> <v-dialog v-model="editor.show.comment" class="max-w-[540px]" eager>
<comment-editor />
</v-dialog>
<v-dialog v-model="editor.show.moment" class="max-w-[540px]" eager>
<moment-editor /> <moment-editor />
</v-dialog> </v-dialog>
<v-dialog v-model="editor.show.comment" class="max-w-[540px]"> <v-dialog v-model="editor.show.article" transition="dialog-bottom-transition" fullscreen eager>
<comment-editor /> <article-editor />
</v-dialog> </v-dialog>
</template> </template>
@ -11,6 +14,7 @@
import { useEditor } from "@/stores/editor" import { useEditor } from "@/stores/editor"
import MomentEditor from "@/components/publish/MomentEditor.vue" import MomentEditor from "@/components/publish/MomentEditor.vue"
import CommentEditor from "@/components/publish/CommentEditor.vue"; import CommentEditor from "@/components/publish/CommentEditor.vue";
import ArticleEditor from "@/components/publish/ArticleEditor.vue";
const editor = useEditor() const editor = useEditor()
</script> </script>

View File

@ -3,10 +3,9 @@
<div class="timeline flex-grow-1 max-w-[75ch]"> <div class="timeline flex-grow-1 max-w-[75ch]">
<v-card :loading="loading"> <v-card :loading="loading">
<article> <article>
<v-card-title>{{ post?.title }}</v-card-title>
<v-card-text> <v-card-text>
<div class="text-sm">{{ post?.description }}</div> <h1 class="text-lg font-medium">{{ post?.title }}</h1>
<p class="text-sm">{{ post?.description }}</p>
<v-divider class="mt-5 mx-[-16px] border-opacity-50" /> <v-divider class="mt-5 mx-[-16px] border-opacity-50" />