mirror of https://github.com/halo-dev/halo
feat: post editor support select attachments
parent
6b1b6db751
commit
87df0ed3cf
|
@ -30,7 +30,6 @@ import { formatDatetime } from "@/utils/date";
|
||||||
import prettyBytes from "pretty-bytes";
|
import prettyBytes from "pretty-bytes";
|
||||||
import { useFetchAttachmentPolicy } from "./composables/use-attachment-policy";
|
import { useFetchAttachmentPolicy } from "./composables/use-attachment-policy";
|
||||||
import { useAttachmentControl } from "./composables/use-attachment";
|
import { useAttachmentControl } from "./composables/use-attachment";
|
||||||
import AttachmentSelectorModal from "@/modules/contents/attachments/components/AttachmentSelectorModal.vue";
|
|
||||||
import AttachmentFileTypeIcon from "./components/AttachmentFileTypeIcon.vue";
|
import AttachmentFileTypeIcon from "./components/AttachmentFileTypeIcon.vue";
|
||||||
import { apiClient } from "@halo-dev/admin-shared";
|
import { apiClient } from "@halo-dev/admin-shared";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
|
@ -41,7 +40,6 @@ import { useFetchAttachmentGroup } from "./composables/use-attachment-group";
|
||||||
const policyVisible = ref(false);
|
const policyVisible = ref(false);
|
||||||
const uploadVisible = ref(false);
|
const uploadVisible = ref(false);
|
||||||
const detailVisible = ref(false);
|
const detailVisible = ref(false);
|
||||||
const selectVisible = ref(false);
|
|
||||||
|
|
||||||
const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true });
|
const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true });
|
||||||
const { groups, handleFetchGroups } = useFetchAttachmentGroup({
|
const { groups, handleFetchGroups } = useFetchAttachmentGroup({
|
||||||
|
@ -178,7 +176,6 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<AttachmentSelectorModal v-model:visible="selectVisible" />
|
|
||||||
<AttachmentDetailModal
|
<AttachmentDetailModal
|
||||||
v-model:visible="detailVisible"
|
v-model:visible="detailVisible"
|
||||||
:attachment="selectedAttachment"
|
:attachment="selectedAttachment"
|
||||||
|
@ -205,7 +202,6 @@ onMounted(() => {
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<VButton size="sm" @click="selectVisible = true"> 选择附件</VButton>
|
|
||||||
<VButton size="sm" @click="policyVisible = true">
|
<VButton size="sm" @click="policyVisible = true">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<IconDatabase2Line class="h-full w-full" />
|
<IconDatabase2Line class="h-full w-full" />
|
||||||
|
|
|
@ -53,7 +53,6 @@ const onVisibleChange = (visible: boolean) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
console.log(Array.from(selected.value));
|
|
||||||
emit("select", Array.from(selected.value));
|
emit("select", Array.from(selected.value));
|
||||||
onVisibleChange(false);
|
onVisibleChange(false);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,16 +14,18 @@ import {
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import PostSettingModal from "./components/PostSettingModal.vue";
|
import PostSettingModal from "./components/PostSettingModal.vue";
|
||||||
import PostPreviewModal from "./components/PostPreviewModal.vue";
|
import PostPreviewModal from "./components/PostPreviewModal.vue";
|
||||||
|
import AttachmentSelectorModal from "../attachments/components/AttachmentSelectorModal.vue";
|
||||||
import type { PostRequest } from "@halo-dev/api-client";
|
import type { PostRequest } from "@halo-dev/api-client";
|
||||||
import { computed, onMounted, ref, watch } from "vue";
|
import { computed, onMounted, ref, watch } from "vue";
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
import { apiClient } from "@halo-dev/admin-shared";
|
import { apiClient, type AttachmentLike } 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";
|
||||||
import {
|
import {
|
||||||
allExtensions,
|
allExtensions,
|
||||||
RichTextEditor,
|
RichTextEditor,
|
||||||
useEditor,
|
useEditor,
|
||||||
|
type Content,
|
||||||
} from "@halo-dev/richtext-editor";
|
} from "@halo-dev/richtext-editor";
|
||||||
import ExtensionCharacterCount from "@tiptap/extension-character-count";
|
import ExtensionCharacterCount from "@tiptap/extension-character-count";
|
||||||
import { formatDatetime } from "@/utils/date";
|
import { formatDatetime } from "@/utils/date";
|
||||||
|
@ -69,6 +71,7 @@ const settingModal = ref(false);
|
||||||
const previewModal = ref(false);
|
const previewModal = ref(false);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const extraActiveId = ref("toc");
|
const extraActiveId = ref("toc");
|
||||||
|
const attachemntSelectorModal = ref(false);
|
||||||
|
|
||||||
const isUpdateMode = computed(() => {
|
const isUpdateMode = computed(() => {
|
||||||
return !!formState.value.post.metadata.creationTimestamp;
|
return !!formState.value.post.metadata.creationTimestamp;
|
||||||
|
@ -104,6 +107,36 @@ watch(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onAttachmentSelect = (attachments: AttachmentLike[]) => {
|
||||||
|
const images: Content[] = attachments.map((attachment) => {
|
||||||
|
const attrs: { src?: string; alt?: string } = {};
|
||||||
|
if (typeof attachment === "string") {
|
||||||
|
attrs.src = attachment;
|
||||||
|
return {
|
||||||
|
type: "image",
|
||||||
|
attrs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if ("url" in attachment) {
|
||||||
|
attrs.src = attachment.url;
|
||||||
|
attrs.alt = attachment.type;
|
||||||
|
}
|
||||||
|
if ("spec" in attachment) {
|
||||||
|
attrs.src = attachment.status?.permalink;
|
||||||
|
attrs.alt = attachment.spec.displayName;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "image",
|
||||||
|
attrs,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
editor.value
|
||||||
|
?.chain()
|
||||||
|
.focus()
|
||||||
|
.insertContent([...images, { type: "paragraph", content: "" }])
|
||||||
|
.run();
|
||||||
|
};
|
||||||
|
|
||||||
const handleGenerateTableOfContent = () => {
|
const handleGenerateTableOfContent = () => {
|
||||||
if (!editor.value) {
|
if (!editor.value) {
|
||||||
return;
|
return;
|
||||||
|
@ -222,12 +255,23 @@ onMounted(async () => {
|
||||||
@saved="onSettingSaved"
|
@saved="onSettingSaved"
|
||||||
/>
|
/>
|
||||||
<PostPreviewModal v-model:visible="previewModal" :post="formState.post" />
|
<PostPreviewModal v-model:visible="previewModal" :post="formState.post" />
|
||||||
|
<AttachmentSelectorModal
|
||||||
|
v-model:visible="attachemntSelectorModal"
|
||||||
|
@select="onAttachmentSelect"
|
||||||
|
/>
|
||||||
<VPageHeader title="文章">
|
<VPageHeader title="文章">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<IconBookRead class="mr-2 self-center" />
|
<IconBookRead class="mr-2 self-center" />
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
|
<VButton
|
||||||
|
size="sm"
|
||||||
|
type="default"
|
||||||
|
@click="attachemntSelectorModal = true"
|
||||||
|
>
|
||||||
|
附件库
|
||||||
|
</VButton>
|
||||||
<VButton size="sm" type="default" @click="previewModal = true">
|
<VButton size="sm" type="default" @click="previewModal = true">
|
||||||
预览
|
预览
|
||||||
</VButton>
|
</VButton>
|
||||||
|
|
Loading…
Reference in New Issue