news 2026/9/10 21:54:20

深入掌握 vue-vben-admin 的 VbenTiptap 富文本编辑器:API、图片上传与自定义扩展实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入掌握 vue-vben-admin 的 VbenTiptap 富文本编辑器:API、图片上传与自定义扩展实战指南

深入掌握 vue-vben-admin 的 VbenTiptap 富文本编辑器:API、图片上传与自定义扩展实战指南

【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin

本文基于 vue-vben-admin 仓库中的 Vben Tiptap Rich Text Editor 文档,结合 tiptap 插件源码 与官方 基础用法 Demo、图片上传 Demo,系统讲解VbenTiptapVbenTiptapPreview两个组件的能力边界、完整 API、工具栏功能、图片上传流程以及自定义扩展机制,让你能够在自己的业务页面中直接复用它,甚至基于它二次封装自己的富文本组件。

组件概览:一个基于 Tiptap 的现代化富文本编辑器

VbenTiptap是 vue-vben-admin 基于 Tiptap 封装的开箱即用富文本编辑器组件,支持富文本格式化、图片插入与图片上传,并内置一套完整的可视化工具栏。与它配套的还有一个只读预览组件VbenTiptapPreview,用于在详情页、卡片等场景展示编辑器产生的 HTML 内容。

组件定义与导出位于 packages/effects/plugins/src/tiptap/index.ts,通过@vben/plugins/tiptap统一对外提供:

export { default as VbenTiptapPreview } from './preview.vue'; export { default as VbenTiptap } from './tiptap.vue'; export * from './types';

所有 Props、事件与扩展相关的 TypeScript 类型均收敛在 packages/effects/plugins/src/tiptap/types.ts,下文介绍的每个配置项都能在这里找到权威定义。

需要特别说明的是:框架提供的组件并不是约束。官方文档明确提示,如果当前实现无法满足需求,可以直接使用原生组件或自行封装,框架组件完全可以按需取舍。

基础用法:最小可运行的富文本表单

VbenTiptap的核心交互模型是v-model,绑定的值即编辑器内容的 HTML 字符串。官方 基础用法 Demo 给出了最小用法:

<script lang="ts" setup> import { ref } from 'vue'; import { VbenTiptap } from '@vben/plugins/tiptap'; const content = ref('<p>开始编辑你的内容...</p>'); </script> <template> <div> <VbenTiptap v-model="content" /> <div class="mt-4"> <p class="text-sm text-gray-500">当前内容:</p> <pre class="mt-2 p-2 bg-gray-100 rounded text-xs overflow-auto max-h-40"> {{ content }} </pre> </div> </div> </template>

从组件源码 tiptap.vue 可以看到v-model的双向同步机制:编辑器onUpdate时读取editor.getHTML(),与当前modelValue不一致才回写,并同时派发change事件;反过来,当外部modelValue变化且与编辑器内容不同时,通过editor.commands.setContent(nextValue, { emitUpdate: false })主动重置内容(见 tiptap.vue)。这样既避免了循环更新,也保证了外部程序化修改内容时编辑器能即时响应。

完整 API 手册

VbenTiptap Props

下表完整收录 官方文档 与 types.ts 中定义的全部 Props:

属性说明类型默认值
modelValue(v-model)编辑器内容(HTML 字符串)string''
editable编辑器是否可编辑booleantrue
toolbar是否显示工具栏booleantrue
previewable是否显示预览按钮booleantrue
placeholder占位提示文本string多语言ui.tiptap.placeholder
minHeight内容区最小高度number \| string240
maxHeight内容区最大高度number \| string400
extensions自定义 Tiptap 扩展Extensions-
imageUpload图片上传配置ImageUploadOptions-

几点从源码确认的细节:

  • 高度支持数字与字符串两种写法。源码中用computed将数字统一转换为 px 字符串(见 tiptap.vue),并通过 CSS 变量--vben-tiptap-min-height/--vben-tiptap-max-height注入内容区样式,也就是说你也可以直接传'300px''50vh'这样的 CSS 尺寸。
  • 占位符默认走国际化。默认值来自$t('ui.tiptap.placeholder')(见 tiptap.vue),内置的多语言文案位于 packages/locales/src/langs 的ui.tiptap命名空间下。
  • editable支持运行时切换。源码通过watch监听props.editable并调用editor.setEditable(editable)(见 tiptap.vue),因此可以配合表单的「编辑/只读」模式动态切换。

VbenTiptap Events

组件只暴露一个change事件,参数类型为VbenTiptapChangeEvent(见 types.ts):

interface VbenTiptapChangeEvent { html: string; // HTML 内容 json: JSONContent; // JSON 结构(ProseMirror 文档树) text: string; // 纯文本内容 }

该事件在编辑器每次内容更新时触发(见 tiptap.vue)。三种数据形态各有用途:html用于提交给后端存储,json适合做结构化数据迁移或程序化分析,text则可以直接用于字数统计或搜索摘要。

VbenTiptapPreview Props

预览组件用于只读展示 HTML 内容,其 Props(见 types.ts 与 官方文档):

属性说明类型默认值
content要预览的 HTML 内容string''
minHeight最小高度number \| string160
class自定义类名any-

实现上,preview.vue 通过v-html渲染内容,并复用与编辑器内容区相同的vben-tiptap-content样式类,保证预览样式与编辑效果一致。因此在使用时必须注意content来自可信来源,避免直接渲染未经净化的用户输入造成 XSS 风险。

工具栏功能全景

工具栏定义在 toolbar.ts 的createToolbarGroups中,按功能分组渲染(分组间以竖线分隔),并在 tiptap.vue 中通过VbenIconButtonVbenPopover组合呈现。工具栏按钮均带有禁用态判断(can校验)与激活态高亮(isActive校验),交互细节完整。

格式化(Formatting)

功能说明底层命令
撤销 / 重做撤销或重做编辑操作undo()/redo()
清除格式移除选中文本的全部格式clearNodes().unsetAllMarks()
加粗粗体toggleBold()
斜体斜体toggleItalic()
下划线下划线toggleUnderline()
删除线删除线toggleStrike()
行内代码行内代码标记toggleCode()

结构(Structure)

功能说明底层命令
标题段落与 H1-H4 标题切换toggleHeading({ level })
有序列表编号列表toggleOrderedList()
无序列表项目符号列表toggleBulletList()
引用块引用样式toggleBlockquote()
代码块多行代码块toggleCodeBlock()

标题按钮是一个带当前状态指示的下拉菜单:源码中getHeadingTriggerText会实时读取光标所在位置的标题级别并显示为P/H1~H4(见 toolbar.ts),激活项右侧会显示Check图标。

链接与图片(Links & Images)

功能说明
插入链接弹出prompt输入框插入或编辑超链接
移除链接移除选中文本的链接
插入图片通过 URL 插入图片

链接处理有一个值得注意的细节:handleLinkAction会先读取当前光标处link的 href 作为默认值回填到输入框,URL 为空时直接unsetLink()移除链接;normalizeLinkUrl会对未带协议的输入自动补全为https://前缀(见 toolbar.ts)。也就是说,用户在弹窗里输入example.com,最终落库的是https://example.com

样式(Style)

功能说明
文字颜色预设调色板设置文字颜色
高亮颜色设置文字背景高亮色

两个颜色工具都通过 Popover 弹出预设调色板(palette)。文字色与高亮色的预设色板定义在 toolbar.ts:基础色使用主题 CSS 变量(foreground/warning/success/destructive),并与@vben/preferences导出的COLOR_PRESETS合并;高亮色在此基础上叠加透明度。当前生效颜色会在工具栏按钮底部以一条小色条(indicatorColor)直观显示,并支持一键「清除」恢复默认。

对齐(Alignment)

功能说明底层命令
左对齐文本左对齐setTextAlign('left')
居中对齐文本居中setTextAlign('center')
右对齐文本右对齐setTextAlign('right')

其他

  • 预览(Preview):点击后通过useVbenModal打开一个全屏可关的模态框,内部用VbenTiptapPreview以 320px 最小高度渲染当前内容快照(见 tiptap.vue),适合在提交前确认排版效果。预览按钮由previewable控制。

图片上传:三种触发方式与完整上传链路

配置imageUpload后,工具栏的图片按钮会从单一的「插入 URL」变为下拉菜单,提供「上传(UPL)」与「URL」两个选项;同时编辑器会启用拖拽与粘贴上传能力。

官方 图片上传 Demo 演示了三种触发方式:

<script lang="ts" setup> import { ref } from 'vue'; import { type ImageUploadOptions, VbenTiptap } from '@vben/plugins/tiptap'; const content = ref(''); // Mock upload function with progress simulation const imageUpload: ImageUploadOptions = { accept: 'image/jpeg,image/png,image/gif,image/webp', maxSize: 5 * 1024 * 1024, // 5MB upload: async (_file, onProgress) => { // Simulate upload progress for (let i = 0; i <= 100; i += 10) { await new Promise((resolve) => setTimeout(resolve, 100)); onProgress?.(i); } // Return a mock image URL (using picsum for demo) return `https://picsum.photos/seed/${Date.now()}/800/400`; }, onUploadError: (error) => { console.error('Upload error:', error); }, }; </script> <template> <div> <VbenTiptap v-model="content" :image-upload="imageUpload" placeholder="尝试拖拽或粘贴图片..." /> </div> </template>

ImageUploadOptions 配置详解

类型定义见 types.ts:

interface ImageUploadOptions { /** 允许的文件类型,默认 'image/*' */ accept?: string; /** 最大文件大小(字节),默认 5MB */ maxSize?: number; /** 上传失败回调,未提供时使用 alert 弹窗提示 */ onUploadError?: (error: unknown) => void; /** 上传函数,返回图片 URL,可选 onProgress 回调报告上传进度 */ upload: ( file: File, onProgress?: (percent: number) => void, ) => Promise<string>; }

三种上传触发方式

  1. 文件选择:点击工具栏「上传」按钮,组件动态创建并点击一个隐藏的<input type="file">(accept 取自配置),选中文件后走上传流程。
  2. 拖拽上传:通过 ProseMirror Plugin 的handleDrop拦截拖拽事件(见 extensions.ts),并计算鼠标落点坐标将图片插入到对应位置。
  3. 粘贴上传:通过handlePaste拦截剪贴板中的图片文件(见 extensions.ts),实现「复制图片 → 直接粘贴进编辑器」的流畅体验。

上传进度与占位展示

上传过程中,组件会在光标位置插入一张使用 blob URL 作为 src 的占位图片,并在其上层叠加加载指示:上传开始时显示 spinner 动画;一旦upload函数通过onProgress回调报告进度(percent > 0),则切换为进度条并实时刷新宽度(见 extensions.ts 的addNodeView实现)。上传完成后,blob URL 会被替换为upload函数返回的真实 URL,并移除data-uploading属性。

文件校验规则

validateFile(见 extensions.ts)实现了两层校验:

  • maxSize:文件字节数超过上限即报「文件过大」;
  • accept:支持逗号分隔的 MIME 类型列表与type/*通配写法(如image/jpeg,image/png),逐项匹配file.type

校验失败会调用onUploadError回调;若未配置,则回退到alert弹窗提示(见 extensions.ts)。

三个必须注意的使用红线

官方文档明确警告以下三点:

  1. 仅支持单图上传:拖拽或粘贴多张图片时,会弹出提示并只处理第一张图片(见 extensions.ts)。
  2. 上传过程中不要保存编辑器内容:此时图片 src 是临时 blob URL,getHTML()拿到的内容无法持久化;应等待上传完成、src 替换为真实 URL 后再提交。
  3. 使用自定义extensions时图片上传功能不可用:工具栏不会显示上传选项(详见下节)。

组件还在销毁时统一执行URL.revokeObjectURL清理所有 blob URL 并销毁编辑器实例(见 tiptap.vue),避免内存泄漏。

自定义扩展:完全掌控编辑器能力

通过extensionsProp 可以传入自定义 Tiptap 扩展数组,完全替换默认扩展集。官方示例:

<script setup lang="ts"> import { VbenTiptap } from '@vben/plugins/tiptap'; import StarterKit from '@tiptap/starter-kit'; import Underline from '@tiptap/extension-underline'; const extensions = [ StarterKit, Underline, // Other extensions... ]; </script> <template> <VbenTiptap v-model="content" :extensions="extensions" /> </template>

传入extensions后,组件将不再调用createDefaultTiptapExtensions,默认扩展配置(标题级别、链接、对齐、颜色、高亮、占位符等)全部失效,你需要自行配置所有必需功能。同时注意两点限制(源码中亦有对应逻辑):

  • 工具栏的图片上传选项只在使用默认扩展时出现:tiptap.vueconst effectiveImageUpload = props.extensions ? undefined : props.imageUpload(见 tiptap.vue),因为自定义扩展可能并不包含uploadImage命令;
  • 工具栏的按钮可用性(can)、激活态(isActive)完全由对应扩展是否注册决定,缺少扩展的按钮会自动置灰或不可用。

源码视角:默认扩展集包含什么

createDefaultTiptapExtensions(见 extensions.ts)由以下部分组成,了解它有助于你在自定义扩展时做能力对照:

扩展关键配置
StarterKit标题级别限制为 H1-H4(heading.levels: [1,2,3,4]),内置 link 关闭(由下方 Link 扩展接管)
TextAlign作用于headingparagraph两类节点
TextStyle+Color文字颜色,作用于textStyle类型
Highlight开启多色高亮(multicolor: true
Linkautolink自动识别链接、defaultProtocol: 'https'openOnClick: false、支持mailto/tel协议
Image/ 自定义 Image默认允许 base64,图片节点附加vben-tiptap__image类名;配置imageUpload时替换为扩展了上传命令与拖拽/粘贴插件的自定义 Image
Placeholder占位文本(支持data-placeholder属性渲染)

场景组合:把组件放进真实表单

将以上能力组合起来,一个典型的「文章编辑 + 详情展示」业务场景可以这样写:

<script lang="ts" setup> import { ref } from 'vue'; import { type ImageUploadOptions, VbenTiptap, VbenTiptapPreview, } from '@vben/plugins/tiptap'; const content = ref(''); const imageUpload: ImageUploadOptions = { maxSize: 2 * 1024 * 1024, upload: async (file, onProgress) => { const formData = new FormData(); formData.append('file', file); // 对接你的真实上传接口,进度通过 onProgress 回传 return await uploadApi(formData, onProgress); }, }; const editable = ref(true); </script> <template> <!-- 编辑态 --> <VbenTiptap v-model="content" :editable="editable" :image-upload="imageUpload" :min-height="320" :max-height="480" placeholder="请输入文章正文..." @change="({ text }) => console.log('字数:', text.length)" /> <!-- 只读展示态 --> <VbenTiptapPreview :content="content" :min-height="200" /> </template>

小结

VbenTiptap系列组件为 vue-vben-admin 项目提供了「开箱即用、可深度定制」的富文本能力:内置完整工具栏覆盖格式化、结构、链接、颜色与对齐等高频编辑需求;imageUpload配置一处即可同时获得选择、拖拽、粘贴三种上传方式与进度反馈;extensions又保留了 Tiptap 生态的全部扩展空间。配合VbenTiptapPreviewchange事件的多形态输出,可以轻松构建从编辑到预览、从存储到统计的完整内容链路。

相关资源:组件文档见 docs/src/en/components/common-ui/vben-tiptap.md,组件实现见 packages/effects/plugins/src/tiptap/,可运行示例见 docs/src/demos/vben-tiptap/basic/index.vue 与 docs/src/demos/vben-tiptap/image-upload/index.vue。

【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 21:47:33

DenseUnet超声甲状腺结节分割实战指南

简介&#xff1a;本资源是一套面向医学图像分割初学者与AI医疗实践者的PyTorch实战项目&#xff0c;聚焦超声甲状腺结节的精准语义分割任务。提供DenseUnet与Unet双网络实现&#xff0c;支持一键训练与推理&#xff0c;内置cosine学习率调度、AdamW优化器及Dice/IoU/Recall/Pre…

作者头像 李华
网站建设 2026/9/10 21:47:29

智能体职业教育的应用现状与技术挑战

1. 智能体职业教育的发展现状与争议 最近两年&#xff0c;智能体职业教育突然成为教育科技领域的热门话题。从最初几家创业公司的小规模尝试&#xff0c;到现在各大教育平台纷纷布局&#xff0c;这个细分领域正在经历爆发式增长。但与此同时&#xff0c;质疑声也不绝于耳&#…

作者头像 李华
网站建设 2026/9/10 21:47:23

SSM框架房屋代管租赁系统设计与实现

1. 项目背景与核心需求 作为一名经历过毕业设计洗礼的老程序员&#xff0c;我深知房屋租赁管理系统这类课题在计算机专业毕业设计中的热门程度。每年都有大量学生选择这个方向&#xff0c;但真正能把系统做完整、做出亮点的却不多。这个基于SSM框架的房屋代管租赁系统&#xff…

作者头像 李华
网站建设 2026/9/10 21:46:43

危化品仓库智能分区与溯源管理实践

1. 危化品仓库管理的痛点与挑战危化品仓库作为特殊物资存储场所&#xff0c;其安全管理一直是行业内的重点难点。我从事化工行业安全管理十余年&#xff0c;见过太多因管理不当引发的事故案例。去年华东某化工厂的爆燃事故&#xff0c;直接经济损失超过2亿元&#xff0c;起因就…

作者头像 李华