feat(@vben/plugins): tiptap 支持图片上传功能

- 新增 imageUpload 配置项,支持自定义上传接口
- 支持文件选择、拖拽、粘贴三种上传方式
- 上传中显示 blob 预览图 + loading spinner / 进度条
- 支持 accept 和 maxSize 文件校验
- 支持 onUploadError 自定义错误处理
- 未配置 imageUpload 时保持原有 URL 插入行为不变
- 使用 NodeView 实现实时 DOM 控制的进度展示
This commit is contained in:
yuan.ji
2026-04-27 13:42:36 +08:00
parent 36d7dc23fa
commit 4ca2f1c6e8
11 changed files with 2727 additions and 2408 deletions

View File

@@ -2,9 +2,13 @@
import { computed, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { VbenTiptap, VbenTiptapPreview } from '@vben/plugins/tiptap';
import {
type ImageUploadOptions,
VbenTiptap,
VbenTiptapPreview,
} from '@vben/plugins/tiptap';
import { Card } from 'ant-design-vue';
import { Card, Switch } from 'ant-design-vue';
const content = ref(`
<h1>Vben Tiptap</h1>
<p>这个编辑器已经被封装在 <code>packages/effects/plugins/src/tiptap</code> 中。</p>
@@ -12,6 +16,35 @@ const content = ref(`
<blockquote>默认内置 StarterKit、Underline、TextAlign、Placeholder。</blockquote>
`);
const previewContent = computed(() => content.value);
const enableUpload = ref(true);
// Mock upload: 模拟上传延迟,支持进度回调
const imageUpload: ImageUploadOptions = {
accept: 'image/*',
maxSize: 5 * 1024 * 1024, // 5MB
upload: (file, onProgress) =>
new Promise((resolve) => {
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 30;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
}
onProgress?.(Math.round(progress));
if (progress >= 100) {
// 上传完成后返回 mock URL
resolve(
`https://picsum.photos/seed/${Date.now()}/640/${Math.round(640 * (file.size % 3 + 2) / 4)}`,
);
}
}, 300);
}),
onUploadError: (error) => {
console.error('Image upload failed:', error);
},
};
</script>
<template>
@@ -23,7 +56,14 @@ const previewContent = computed(() => content.value);
</template>
<Card class="mb-5" title="编辑器">
<VbenTiptap v-model="content" />
<div class="mb-3 flex items-center gap-3">
<span class="text-sm">启用图片上传:</span>
<Switch v-model:checked="enableUpload" />
</div>
<VbenTiptap
v-model="content"
:image-upload="enableUpload ? imageUpload : undefined"
/>
</Card>
<Card class="mb-5" title="富文本预览">