From 3eb9d165bf6febe1eeaf9e4cf2b3f70a013d55b5 Mon Sep 17 00:00:00 2001 From: Takagi <1103069291@qq.com> Date: Tue, 27 Feb 2024 12:00:13 +0800 Subject: [PATCH] pref: refactor default editor video block upload logic (#5302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area console /area editor /milestone 2.13.x #### What this PR does / why we need it: 重构默认编辑器视频组件。使其支持上传、取消上传、从附件库选择、替换等功能。 #### How to test it? 直接拖动、复制或选择文件上传一个视频,查看是否显示上传进度条,取消、重试功能是否正常 #### Which issue(s) this PR fixes: Fixes #5240 #### Does this PR introduce a user-facing change? ```release-note 重构编辑器视频组件的上传逻辑,增加选择文件上传、上传进度条、取消、重试等机制。 ``` --- .../editor/src/extensions/video/index.ts | 2 +- ui/src/components/editor/DefaultEditor.vue | 12 +- .../editor/components/EditorLinkObtain.vue | 224 ++++++++++++++++++ ui/src/components/editor/components/index.ts | 1 + .../editor/extensions/image/ImageView.vue | 215 ++++------------- ui/src/components/editor/extensions/index.ts | 3 +- .../editor/extensions/upload/index.ts | 4 + .../editor/extensions/video/VideoView.vue | 209 ++++++++++++++++ .../editor/extensions/video/index.ts | 36 +++ ui/src/components/editor/utils/upload.ts | 19 ++ ui/src/locales/en.yaml | 9 +- ui/src/locales/zh-CN.yaml | 9 +- ui/src/locales/zh-TW.yaml | 9 +- 13 files changed, 569 insertions(+), 183 deletions(-) create mode 100644 ui/src/components/editor/components/EditorLinkObtain.vue create mode 100644 ui/src/components/editor/components/index.ts create mode 100644 ui/src/components/editor/extensions/video/VideoView.vue create mode 100644 ui/src/components/editor/extensions/video/index.ts diff --git a/ui/packages/editor/src/extensions/video/index.ts b/ui/packages/editor/src/extensions/video/index.ts index 76936a850..df661cec6 100644 --- a/ui/packages/editor/src/extensions/video/index.ts +++ b/ui/packages/editor/src/extensions/video/index.ts @@ -96,7 +96,7 @@ const Video = Node.create({ }, }, controls: { - default: null, + default: true, parseHTML: (element) => { return element.getAttribute("controls"); }, diff --git a/ui/src/components/editor/DefaultEditor.vue b/ui/src/components/editor/DefaultEditor.vue index cbead8137..0a584e9bc 100644 --- a/ui/src/components/editor/DefaultEditor.vue +++ b/ui/src/components/editor/DefaultEditor.vue @@ -52,7 +52,11 @@ import { ExtensionSearchAndReplace, } from "@halo-dev/richtext-editor"; // ui custom extension -import { UiExtensionImage, UiExtensionUpload } from "./extensions"; +import { + UiExtensionImage, + UiExtensionUpload, + UiExtensionVideo, +} from "./extensions"; import { IconCalendar, IconCharacterRecognition, @@ -263,7 +267,11 @@ onMounted(() => { lowlight, }), ExtensionIframe, - ExtensionVideo, + currentUserHasPermission(["uc:attachments:manage"]) + ? UiExtensionVideo.configure({ + uploadVideo: props.uploadImage, + }) + : ExtensionVideo, ExtensionAudio, ExtensionCharacterCount, ExtensionFontSize, diff --git a/ui/src/components/editor/components/EditorLinkObtain.vue b/ui/src/components/editor/components/EditorLinkObtain.vue new file mode 100644 index 000000000..2fa867bb9 --- /dev/null +++ b/ui/src/components/editor/components/EditorLinkObtain.vue @@ -0,0 +1,224 @@ + + diff --git a/ui/src/components/editor/components/index.ts b/ui/src/components/editor/components/index.ts new file mode 100644 index 000000000..181dee03c --- /dev/null +++ b/ui/src/components/editor/components/index.ts @@ -0,0 +1 @@ +export { default as EditorLinkObtain } from "./EditorLinkObtain.vue"; diff --git a/ui/src/components/editor/extensions/image/ImageView.vue b/ui/src/components/editor/extensions/image/ImageView.vue index 1eefaf4c8..15f20ca3f 100644 --- a/ui/src/components/editor/extensions/image/ImageView.vue +++ b/ui/src/components/editor/extensions/image/ImageView.vue @@ -8,20 +8,10 @@ import { import { NodeViewWrapper } from "@halo-dev/richtext-editor"; import { computed, onMounted, ref } from "vue"; import Image from "./index"; -import { watch } from "vue"; -import { fileToBase64, uploadFile } from "../../utils/upload"; -import type { Attachment } from "@halo-dev/api-client"; -import type { AttachmentLike } from "@halo-dev/console-shared"; -import { useFileDialog } from "@vueuse/core"; -import { onUnmounted } from "vue"; -import { - VButton, - VSpace, - IconImageAddLine, - VDropdown, -} from "@halo-dev/components"; -import { getAttachmentUrl } from "../../utils/attachment"; -import { i18n } from "@/locales"; +import { fileToBase64 } from "../../utils/upload"; +import { VButton, IconImageAddLine } from "@halo-dev/components"; +import { type AttachmentAttr } from "../../utils/attachment"; +import { EditorLinkObtain } from "../../components"; const props = defineProps<{ editor: Editor; @@ -63,98 +53,46 @@ const href = computed({ }, }); -const originalFile = ref(); const fileBase64 = ref(); const uploadProgress = ref(undefined); const retryFlag = ref(false); -const controller = ref(); -const initSrc = ref(); +const editorLinkObtain = ref(); + +const handleUploadAbort = () => { + editorLinkObtain.value?.abort(); +}; const initialization = computed(() => { return !src.value && !fileBase64.value; }); -const handleEnterSetSrc = () => { - if (!initSrc.value) { - return; - } - props.updateAttributes({ src: initSrc.value }); -}; - -const openAttachmentSelector = () => { - props.editor.commands.openAttachmentSelector( - (attachments: AttachmentLike[]) => { - if (attachments.length > 0) { - const attachment = attachments[0]; - const attachmentAttr = getAttachmentUrl(attachment); - props.updateAttributes({ - src: attachmentAttr.url, - alt: attachmentAttr.name, - }); - } - }, - { - accepts: ["image/*"], - min: 1, - max: 1, - } - ); -}; - -const { open, reset, onChange } = useFileDialog({ - accept: "image/*", - multiple: false, -}); - -const handleUploadAbort = () => { - if (!controller.value) { - return; - } - controller.value.abort(); - resetUpload(); -}; - -const handleUploadRetry = () => { - if (!originalFile.value) { - return; - } - handleUploadImage(originalFile.value); -}; - -const handleUploadImage = async (file: File) => { - originalFile.value = file; +const handleUploadReady = async (file: File) => { fileBase64.value = await fileToBase64(file); retryFlag.value = false; - controller.value = new AbortController(); - uploadFile(file, props.extension.options.uploadImage, { - controller: controller.value, - onUploadProgress: (progress) => { - uploadProgress.value = progress; - }, +}; - onFinish: (attachment?: Attachment) => { - if (attachment) { - props.updateAttributes({ - src: attachment.status?.permalink, - }); - } - - resetUpload(); - }, - - onError: () => { - retryFlag.value = true; - }, +const handleSetExternalLink = (attachment: AttachmentAttr) => { + props.updateAttributes({ + src: attachment.url, + alt: attachment.name, }); }; +const handleUploadRetry = () => { + editorLinkObtain.value?.retry(); +}; + +const handleUploadProgress = (progress: number) => { + uploadProgress.value = progress; +}; + +const handleUploadError = () => { + retryFlag.value = true; +}; + const resetUpload = () => { - reset(); - originalFile.value = undefined; fileBase64.value = undefined; uploadProgress.value = undefined; - controller.value?.abort(); - controller.value = undefined; if (props.getPos()) { props.updateAttributes({ width: undefined, @@ -164,35 +102,13 @@ const resetUpload = () => { }; const handleResetInit = () => { - resetUpload(); + editorLinkObtain.value?.reset(); props.updateAttributes({ src: "", file: undefined, }); }; -onChange((files) => { - if (!files) { - return; - } - if (files.length > 0) { - handleUploadImage(files[0]); - } -}); - -watch( - () => props.node?.attrs.file, - async (file) => { - if (!file) { - return; - } - handleUploadImage(file); - }, - { - immediate: true, - } -); - const aspectRatio = ref(0); const resizeRef = ref(); @@ -238,10 +154,6 @@ onMounted(() => { document.documentElement.removeEventListener("mouseup", stopDrag, false); } }); - -onUnmounted(() => { - handleUploadAbort(); -});