diff --git a/packages/editor/src/extensions/image/BubbleItemImageHref.vue b/packages/editor/src/extensions/image/BubbleItemImageHref.vue new file mode 100644 index 0000000..ce074a8 --- /dev/null +++ b/packages/editor/src/extensions/image/BubbleItemImageHref.vue @@ -0,0 +1,37 @@ + + + diff --git a/packages/editor/src/extensions/image/ImageView.vue b/packages/editor/src/extensions/image/ImageView.vue index 249103a..5ba6c21 100644 --- a/packages/editor/src/extensions/image/ImageView.vue +++ b/packages/editor/src/extensions/image/ImageView.vue @@ -38,6 +38,14 @@ const alt = computed({ }, }); +const href = computed({ + get: () => { + return props.node?.attrs.href; + }, + set: (href: string) => { + props.updateAttributes({ href: href }); + }, +}); function handleSetFocus() { props.editor.commands.setNodeSelection(props.getPos()); } @@ -93,6 +101,7 @@ onMounted(() => { :src="src" :title="node.attrs.title" :alt="alt" + :href="href" class="w-full h-full" /> diff --git a/packages/editor/src/extensions/image/index.ts b/packages/editor/src/extensions/image/index.ts index 116ecab..fcc580f 100644 --- a/packages/editor/src/extensions/image/index.ts +++ b/packages/editor/src/extensions/image/index.ts @@ -1,5 +1,5 @@ import TiptapImage from "@tiptap/extension-image"; -import { isActive, type Editor } from "@tiptap/core"; +import { isActive, mergeAttributes, type Editor } from "@tiptap/core"; import { VueNodeViewRenderer } from "@tiptap/vue-3"; import ImageView from "./ImageView.vue"; import type { ImageOptions } from "@tiptap/extension-image"; @@ -12,6 +12,7 @@ import type { EditorState } from "@tiptap/pm/state"; import BubbleItemImageSize from "./BubbleItemImageSize.vue"; import BubbleItemImageAlt from "./BubbleItemImageAlt.vue"; import BubbleItemVideoLink from "./BubbleItemImageLink.vue"; +import BubbleItemImageHref from "./BubbleItemImageHref.vue"; import { BlockActionSeparator } from "@/components"; import MdiFormatAlignLeft from "~icons/mdi/format-align-left"; import MdiFormatAlignCenter from "~icons/mdi/format-align-center"; @@ -22,6 +23,7 @@ import MdiShare from "~icons/mdi/share"; import MdiTextBoxEditOutline from "~icons/mdi/text-box-edit-outline"; import MdiDeleteForeverOutline from "~icons/mdi/delete-forever-outline?color=red"; import { deleteNode } from "@/utils"; +import MdiLink from "~icons/mdi/link"; const Image = TiptapImage.extend({ inline() { @@ -61,6 +63,18 @@ const Image = TiptapImage.extend({ }; }, }, + href: { + default: null, + parseHTML: (element) => { + const href = element.getAttribute("href") || null; + return href; + }, + renderHTML: (attributes) => { + return { + href: attributes.href, + }; + }, + }, style: { renderHTML() { return { @@ -84,7 +98,6 @@ const Image = TiptapImage.extend({ }, ]; }, - addOptions() { return { ...this.parent?.(), @@ -186,10 +199,20 @@ const Image = TiptapImage.extend({ }, { priority: 100, - component: markRaw(BlockActionSeparator), + props: { + icon: markRaw(MdiLink), + title: i18n.global.t("editor.extensions.image.edit_href"), + action: () => { + return markRaw(BubbleItemImageHref); + }, + }, }, { priority: 110, + component: markRaw(BlockActionSeparator), + }, + { + priority: 120, props: { icon: markRaw(MdiDeleteForeverOutline), title: i18n.global.t("editor.common.button.delete"), @@ -201,6 +224,16 @@ const Image = TiptapImage.extend({ }, }; }, + renderHTML({ HTMLAttributes }) { + if (HTMLAttributes.href) { + return [ + "a", + { href: HTMLAttributes.href }, + ["img", mergeAttributes(HTMLAttributes)], + ]; + } + return ["img", mergeAttributes(HTMLAttributes)]; + }, }); const handleSetTextAlign = ( diff --git a/packages/editor/src/locales/en.yaml b/packages/editor/src/locales/en.yaml index 9869358..34ea7a5 100644 --- a/packages/editor/src/locales/en.yaml +++ b/packages/editor/src/locales/en.yaml @@ -52,6 +52,7 @@ editor: restore_size: Restore original size edit_link: Edit link edit_alt: Edit alt + edit_href: Edit the image hyperlink video: disable_controls: Hide controls enable_controls: Show controls @@ -107,6 +108,7 @@ editor: placeholder: link_input: Enter the link and press enter to confirm. alt_input: Enter the image alt and press enter to confirm. + alt_href: Enter the image hyperlink and press enter to confirm. button: new_line: New line delete: Delete diff --git a/packages/editor/src/locales/zh-CN.yaml b/packages/editor/src/locales/zh-CN.yaml index 6103894..5146bdc 100644 --- a/packages/editor/src/locales/zh-CN.yaml +++ b/packages/editor/src/locales/zh-CN.yaml @@ -52,6 +52,7 @@ editor: restore_size: 恢复原始尺寸 edit_link: 修改链接 edit_alt: 修改图片 alt 属性 + edit_href: 修改图片跳转链接 video: disable_controls: 隐藏控制面板 enable_controls: 显示控制面板 @@ -107,6 +108,7 @@ editor: placeholder: link_input: 输入链接,按回车确定 alt_input: 输入图片 alt 属性值,按回车确认 + alt_href: 输入图片跳转链接,按回车确认 button: new_line: 换行 delete: 删除