refactor: post editing

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/603/head
Ryan Wang 2022-08-23 17:10:00 +08:00
parent dc101e8183
commit fd56f24b1f
6 changed files with 96 additions and 79 deletions

View File

@ -14,8 +14,6 @@ import { apiClient } from "@halo-dev/admin-shared";
import { useRouteQuery } from "@vueuse/router"; import { useRouteQuery } from "@vueuse/router";
import { v4 as uuid } from "uuid"; import { v4 as uuid } from "uuid";
const name = useRouteQuery("name");
const initialFormState: PostRequest = { const initialFormState: PostRequest = {
post: { post: {
spec: { spec: {
@ -60,10 +58,13 @@ const isUpdateMode = computed(() => {
return !!formState.value.post.metadata.creationTimestamp; return !!formState.value.post.metadata.creationTimestamp;
}); });
const handleSavePost = async () => { const handleSave = async () => {
try { try {
saving.value = true; saving.value = true;
// Set rendered content
formState.value.content.content = formState.value.content.raw; formState.value.content.content = formState.value.content.raw;
if (isUpdateMode.value) { if (isUpdateMode.value) {
const { data } = await apiClient.post.updateDraftPost( const { data } = await apiClient.post.updateDraftPost(
formState.value.post.metadata.name, formState.value.post.metadata.name,
@ -75,20 +76,38 @@ const handleSavePost = async () => {
formState.value.post = data; formState.value.post = data;
name.value = data.metadata.name; name.value = data.metadata.name;
} }
await handleFetchContent();
} catch (e) { } catch (e) {
alert(`保存异常: ${e}`);
console.error("Failed to save post", e); console.error("Failed to save post", e);
} finally { } finally {
saving.value = false; saving.value = false;
} }
}; };
const onSettingSaved = (post: PostRequest) => { const handleFetchContent = async () => {
formState.value = post; if (!formState.value.post.spec.headSnapshot) {
settingModal.value = false; return;
handleSavePost(); }
const { data } = await apiClient.content.obtainSnapshotContent(
formState.value.post.spec.headSnapshot
);
formState.value.content = data;
}; };
const onSettingSaved = (post: PostRequest) => {
// Set route query parameter
if (!isUpdateMode.value) {
name.value = post.post.metadata.name;
}
formState.value = post;
settingModal.value = false;
};
// Get post data when the route contains the name parameter
const name = useRouteQuery("name");
onMounted(async () => { onMounted(async () => {
if (name.value) { if (name.value) {
// fetch post // fetch post
@ -98,12 +117,8 @@ onMounted(async () => {
); );
formState.value.post = post; formState.value.post = post;
if (formState.value.post.spec.headSnapshot) { // fetch post content
const { data: content } = await apiClient.content.obtainSnapshotContent( await handleFetchContent();
formState.value.post.spec.headSnapshot
);
formState.value.content = content;
}
} }
}); });
</script> </script>
@ -111,7 +126,6 @@ onMounted(async () => {
<template> <template>
<PostSettingModal <PostSettingModal
v-model:visible="settingModal" v-model:visible="settingModal"
:only-emit="true"
:post="formState" :post="formState"
@saved="onSettingSaved" @saved="onSettingSaved"
/> />
@ -121,12 +135,7 @@ onMounted(async () => {
</template> </template>
<template #actions> <template #actions>
<VSpace> <VSpace>
<VButton <VButton :loading="saving" size="sm" type="default" @click="handleSave">
:loading="saving"
size="sm"
type="default"
@click="handleSavePost"
>
保存 保存
</VButton> </VButton>
<VButton type="secondary" @click="settingModal = true"> <VButton type="secondary" @click="settingModal = true">

View File

@ -101,13 +101,17 @@ const handlePaginationChange = ({
handleFetchPosts(); handleFetchPosts();
}; };
const handleOpenSettingModal = (post: Post) => { const handleOpenSettingModal = async (post: Post) => {
selectedPost.value = post; const { data } = await apiClient.extension.post.getcontentHaloRunV1alpha1Post(
post.metadata.name
);
selectedPost.value = data;
settingModal.value = true; settingModal.value = true;
}; };
const onSettingModalClose = () => { const onSettingModalClose = () => {
selectedPost.value = null; selectedPost.value = null;
selectedPostWithContent.value = null;
handleFetchPosts(); handleFetchPosts();
}; };
@ -117,7 +121,11 @@ const handleSelectPrevious = async () => {
(post) => post.post.metadata.name === selectedPost.value?.metadata.name (post) => post.post.metadata.name === selectedPost.value?.metadata.name
); );
if (index > 0) { if (index > 0) {
selectedPost.value = items[index - 1].post; const { data } =
await apiClient.extension.post.getcontentHaloRunV1alpha1Post(
items[index - 1].post.metadata.name
);
selectedPost.value = data;
return; return;
} }
if (index === 0 && hasPrevious) { if (index === 0 && hasPrevious) {
@ -133,7 +141,11 @@ const handleSelectNext = async () => {
(post) => post.post.metadata.name === selectedPost.value?.metadata.name (post) => post.post.metadata.name === selectedPost.value?.metadata.name
); );
if (index < items.length - 1) { if (index < items.length - 1) {
selectedPost.value = items[index + 1].post; const { data } =
await apiClient.extension.post.getcontentHaloRunV1alpha1Post(
items[index + 1].post.metadata.name
);
selectedPost.value = data;
return; return;
} }
if (index === items.length - 1 && hasNext) { if (index === items.length - 1 && hasNext) {
@ -625,7 +637,7 @@ function handlePhaseFilterItemChange(filterItem: FilterItem) {
<template #actions> <template #actions>
<VSpace> <VSpace>
<VButton @click="handleFetchPosts"></VButton> <VButton @click="handleFetchPosts"></VButton>
<VButton type="primary" :route="{ name: 'PostEditor' }"> <VButton :route="{ name: 'PostEditor' }" type="primary">
<template #icon> <template #icon>
<IconAddCircle class="h-full w-full" /> <IconAddCircle class="h-full w-full" />
</template> </template>

View File

@ -88,6 +88,12 @@ const onVisibleChange = (visible: boolean) => {
} }
}; };
const handleResetForm = () => {
formState.value = cloneDeep(initialFormState);
formState.value.metadata.name = uuid();
reset("category-form");
};
const { Command_Enter } = useMagicKeys(); const { Command_Enter } = useMagicKeys();
watchEffect(() => { watchEffect(() => {
@ -99,13 +105,20 @@ watchEffect(() => {
watch( watch(
() => props.visible, () => props.visible,
(visible) => { (visible) => {
if (visible && props.category) { if (!visible) {
formState.value = cloneDeep(props.category); handleResetForm();
return; }
}
);
watch(
() => props.category,
(category) => {
if (category) {
formState.value = cloneDeep(category);
} else {
handleResetForm();
} }
formState.value = cloneDeep(initialFormState);
reset("category-form");
formState.value.metadata.name = uuid();
} }
); );
</script> </script>

View File

@ -88,7 +88,9 @@ function onDelete(category: CategoryTree) {
</div> </div>
<template #popper> 删除中</template> <template #popper> 删除中</template>
</FloatingTooltip> </FloatingTooltip>
<!--TODO: Get post count-->
<div <div
v-if="false"
class="cursor-pointer text-sm text-gray-500 hover:text-gray-900" class="cursor-pointer text-sm text-gray-500 hover:text-gray-900"
> >
20 篇文章 20 篇文章

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { VButton, VModal, VSpace, VTabItem, VTabs } from "@halo-dev/components"; import { VButton, VModal, VSpace, VTabItem, VTabs } from "@halo-dev/components";
import { computed, ref, watch, watchEffect } from "vue"; import { computed, ref, watchEffect } from "vue";
import type { PostRequest } from "@halo-dev/api-client"; import type { PostRequest } from "@halo-dev/api-client";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag"; import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
@ -48,12 +48,10 @@ const props = withDefaults(
defineProps<{ defineProps<{
visible: boolean; visible: boolean;
post?: PostRequest | null; post?: PostRequest | null;
onlyEmit?: boolean;
}>(), }>(),
{ {
visible: false, visible: false,
post: null, post: null,
onlyEmit: false,
} }
); );
@ -100,11 +98,7 @@ const handleVisibleChange = (visible: boolean) => {
} }
}; };
const handleSaveOnly = async () => { const handleSave = async () => {
if (props.onlyEmit) {
emit("saved", formState.value);
return;
}
try { try {
saving.value = true; saving.value = true;
if (isUpdateMode.value) { if (isUpdateMode.value) {
@ -129,13 +123,24 @@ const handleSaveOnly = async () => {
const handlePublish = async () => { const handlePublish = async () => {
try { try {
publishing.value = true; publishing.value = true;
// Save post
await handleSave();
// Get latest version post
const { data: latestData } =
await apiClient.extension.post.getcontentHaloRunV1alpha1Post(
formState.value.post.metadata.name
);
formState.value.post = latestData;
// Publish post
const { data } = await apiClient.post.publishPost( const { data } = await apiClient.post.publishPost(
formState.value.post.metadata.name formState.value.post.metadata.name
); );
formState.value.post = data; formState.value.post = data;
emit("saved", formState.value); emit("saved", formState.value);
} catch (e) { } catch (e) {
alert(`发布异常: ${e}`);
console.error("Failed to publish post", e); console.error("Failed to publish post", e);
} finally { } finally {
publishing.value = false; publishing.value = false;
@ -145,15 +150,22 @@ const handlePublish = async () => {
const handlePublishCanceling = async () => { const handlePublishCanceling = async () => {
try { try {
publishCanceling.value = true; publishCanceling.value = true;
// Update published spec = false
const postToUpdate = cloneDeep(formState.value); const postToUpdate = cloneDeep(formState.value);
postToUpdate.post.spec.published = false; postToUpdate.post.spec.published = false;
await apiClient.post.updateDraftPost(
const { data } = await apiClient.post.updateDraftPost(
postToUpdate.post.metadata.name, postToUpdate.post.metadata.name,
postToUpdate postToUpdate
); );
formState.value.post = data; // Get latest version post
const { data: latestData } =
await apiClient.extension.post.getcontentHaloRunV1alpha1Post(
formState.value.post.metadata.name
);
formState.value.post = latestData;
emit("saved", formState.value); emit("saved", formState.value);
} catch (e) { } catch (e) {
console.log("Failed to cancel publish", e); console.log("Failed to cancel publish", e);
@ -162,18 +174,6 @@ const handlePublishCanceling = async () => {
} }
}; };
watch(
() => props.visible,
(visible) => {
if (visible && props.post) {
formState.value = cloneDeep(props.post);
}
if (!visible) {
// TODO
}
}
);
watchEffect(() => { watchEffect(() => {
if (props.post) { if (props.post) {
formState.value = cloneDeep(props.post); formState.value = cloneDeep(props.post);
@ -288,27 +288,7 @@ watchEffect(() => {
></FormKit> ></FormKit>
</FormKit> </FormKit>
</VTabItem> </VTabItem>
<VTabItem id="seo" label="SEO"> <!--TODO: add SEO/Metas/Inject Code form-->
<FormKit id="seo" :actions="false" :preserve="true" type="form">
<FormKit
label="自定义关键词"
name="metaKeywords"
type="textarea"
></FormKit>
<FormKit
label="自定义描述"
name="metaDescription"
type="textarea"
></FormKit>
</FormKit>
</VTabItem>
<VTabItem id="metas" label="元数据"></VTabItem>
<VTabItem id="inject-code" label="代码注入">
<FormKit id="inject-code" :actions="false" :preserve="true" type="form">
<FormKit label="CSS" type="textarea"></FormKit>
<FormKit label="JavaScript" type="textarea"></FormKit>
</FormKit>
</VTabItem>
</VTabs> </VTabs>
<template #footer> <template #footer>
@ -323,7 +303,6 @@ watchEffect(() => {
</VButton> </VButton>
<VButton <VButton
v-else v-else
:disabled="!isUpdateMode"
:loading="publishing" :loading="publishing"
type="secondary" type="secondary"
@click="handlePublish" @click="handlePublish"
@ -334,7 +313,7 @@ watchEffect(() => {
:loading="saving" :loading="saving"
size="sm" size="sm"
type="secondary" type="secondary"
@click="handleSaveOnly" @click="handleSave"
> >
仅保存 仅保存
</VButton> </VButton>

View File

@ -207,7 +207,9 @@ onMounted(async () => {
</div> </div>
<template #popper> 删除中</template> <template #popper> 删除中</template>
</FloatingTooltip> </FloatingTooltip>
<!--TODO: Get post count-->
<div <div
v-if="false"
class="cursor-pointer text-sm text-gray-500 hover:text-gray-900" class="cursor-pointer text-sm text-gray-500 hover:text-gray-900"
> >
20 篇文章 20 篇文章