基于35K star+富文本编辑器框架tiptap实现一个自已报表设计器可行

Tiptap 是一个基于 ProseMirror 的现代化、可扩展的富文本编辑器框架,专为构建功能丰富的文本编辑器而设计。它不是一个开箱即用的"Word 替代品",而是一个强大的开发工具包,让开发者可以高度定制地构建自己需要的编辑器。

简单来说,Tiptap 能让你在 Web 应用中构建出类似于 Notion、Trello、Linear、GitHub 评论框、CMS 后台编辑器 等产品中的那种交互式文本编辑体验。

Tiptap 的主要能力和应用场景:

  1. 核心文本编辑与格式化

基础富文本功能:加粗、斜体、下划线、删除线、代码块等。

标题、段落、列表(有序/无序)、任务列表、引用块。

文本对齐、行高、缩进。

  1. 高级内容类型与布局

表格:支持创建、编辑、调整表格。

代码块:支持语法高亮(配合 Prism.js 或 Highlight.js)。

图片与媒体:上传、拖拽插入、调整大小、对齐。

水平分割线。

可定制的"文档":类似 Notion,可以嵌入多种内容块。

  1. 协同编辑

内置对 Y.js 协议的支持,可以相对容易地实现实时协同编辑(多人同时编辑同一文档,看到彼此的光标和更改),这是其核心亮点之一。

  1. 强大的扩展性

核心能力:Tiptap 本身非常精简,所有高级功能(如表单、图片、表格等)都通过扩展(Extension) 来实现。你可以选择你需要的功能,保持编辑器轻量。

自定义节点和标记:你可以创建自己的内容类型,比如"商品卡片"、"用户提及"、"特殊注解块"等。

自定义插件:可以注入任何功能,如快捷键、拖放处理、粘贴规则等。

  1. 与现代技术栈无缝集成

专为 Vue.js、React、Svelte 和原生 JavaScript 项目设计,有对应的官方包(@tiptap/vue-3, @tiptap/react, @tiptap/svelte)。

状态管理清晰,易于与你的应用状态(如 Vuex/Pinia, Redux, Zustand)集成。

  1. 出色的用户体验功能

气泡菜单:选中文本时出现的浮动工具栏(类似 Medium)。

浮动菜单:在空行或特定位置出现的"+"号工具菜单(类似 Notion)。

斜杠命令:输入 / 唤起命令菜单,快速插入元素(非常流行的交互模式)。

历史记录:撤销/重做。

Markdown 快捷键:在输入时使用 Markdown 语法(如 **加粗**、# 标题)快速格式化。

  1. 数据输出灵活

可以输出 HTML、JSON、Markdown 或纯文本。其原生文档模型是基于 JSON 的,这使得保存、传输和恢复复杂内容(包括自定义节点)非常方便。

拖拉表格行

设置行数据绑定

简单的使用例子

复制代码
<template>
  <div class="editor">
    <div class="menu-bar">
      <button
        @click="editor.chain().focus().undo().run()"
        :disabled="!editor.can().undo()"
      >
        撤销
      </button>
      <button
        @click="editor.chain().focus().redo().run()"
        :disabled="!editor.can().redo()"
      >
        重做
      </button>
      <button
        @click="editor.chain().focus().setParagraph().run()"
        :class="{ 'is-active': editor.isActive('paragraph') }"
      >
        段落
      </button>
      <button
        @click="editor.chain().focus().toggleHeading({ level: 1 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
      >
        H1
      </button>
      <button
        @click="editor.chain().focus().toggleHeading({ level: 2 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
      >
        H2
      </button>
      <button
        @click="editor.chain().focus().toggleHeading({ level: 3 }).run()"
        :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
      >
        H3
      </button>
      <button
        @click="editor.chain().focus().toggleBold().run()"
        :class="{ 'is-active': editor.isActive('bold') }"
      >
        粗体
      </button>
      <button
        @click="editor.chain().focus().toggleItalic().run()"
        :class="{ 'is-active': editor.isActive('italic') }"
      >
        斜体
      </button>
      <button
        @click="editor.chain().focus().toggleStrike().run()"
        :class="{ 'is-active': editor.isActive('strike') }"
      >
        删除线
      </button>
      <button
        @click="editor.chain().focus().toggleBulletList().run()"
        :class="{ 'is-active': editor.isActive('bulletList') }"
      >
        无序列表
      </button>
      <button
        @click="editor.chain().focus().toggleOrderedList().run()"
        :class="{ 'is-active': editor.isActive('orderedList') }"
      >
        有序列表
      </button>
      <button
        @click="editor.chain().focus().toggleBlockquote().run()"
        :class="{ 'is-active': editor.isActive('blockquote') }"
      >
        引用
      </button>
      <button @click="editor.chain().focus().setHorizontalRule().run()">
        水平线
      </button>
      <button @click="editor.chain().focus().setHardBreak().run()">
        换行
      </button>
      <button @click="editor.chain().focus().unsetAllMarks().run()">
        清除格式
      </button>
    </div>
    
    <editor-content :editor="editor" />
    
    <div class="content" v-if="editor">
      <h3>编辑器内容 (JSON):</h3>
      <pre>{{ editor.getJSON() }}</pre>
      
      <h3>编辑器内容 (HTML):</h3>
      <pre>{{ editor.getHTML() }}</pre>
    </div>
  </div>
</template>

<script>
import { Editor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

export default {
  components: {
    EditorContent,
  },

  data() {
    return {
      editor: null,
    }
  },

  mounted() {
    this.editor = new Editor({
      content: `
        <h2>欢迎使用 Tiptap!</h2>
        <p>这是一个功能丰富的富文本编辑器,基于 Vue.js 和 ProseMirror 构建。</p>
        <p>尝试使用上面的菜单来格式化文本,或者输入 <code>/</code> 来查看斜杠命令。</p>
      `,
      extensions: [
        StarterKit,
      ],
      onUpdate: () => {
        // 编辑器内容更新时触发
        // 可以在这里添加保存逻辑
      },
    })
  },

  beforeUnmount() {
    this.editor.destroy()
  },
}
</script>

<style>
.editor {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.menu-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid #eee;
}

.menu-bar button {
  padding: 5px 10px;
  background: #f5f5f5;
  border: 1px solid #ddd;
  border-radius: 3px;
  cursor: pointer;
}

.menu-bar button:hover {
  background: #eee;
}

.menu-bar button.is-active {
  background: #4a90e2;
  color: white;
  border-color: #4a90e2;
}

.ProseMirror {
  min-height: 200px;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
}

.ProseMirror:focus {
  outline: none;
  border-color: #4a90e2;
}

.content {
  margin-top: 20px;
  padding: 10px;
  background: #f9f9f9;
  border-radius: 4px;
}

.content pre {
  white-space: pre-wrap;
  background: #f0f0f0;
  padding: 10px;
  border-radius: 4px;
}
</style>
相关推荐
亮子AI4 天前
【Tiptap】在服务器端使用 Tiptap 内容格式转换
tiptap
亮子AI1 个月前
【Tiptap】Tiptap 和 ProseMirror 选哪个?
tiptap
亮子AI1 个月前
【Tiptap】如何使用 ordered list?
数据结构·list·tiptap
亮子AI1 个月前
【Tiptap】怎样高效存储内容?
tiptap
亮子AI1 个月前
【Tiptap】怎样输入/粘贴 Markdown 到编辑器里?
编辑器·tiptap
亮子AI1 个月前
【Tiptap】如何实现增量更新?
tiptap
HBR666_3 个月前
AI编辑器(FIM补全,AI扩写)简介
前端·ai·编辑器·fim·tiptap
HBR666_3 个月前
AI编辑器(二) ---调用模型的fim功能
前端·ai·编辑器·fim·tiptap
不老刘5 个月前
Tiptap(基于 Prosemirror)vs TinyMCE:哪个更适合你的技术栈?
编辑器·tinymce·tiptap·prosemirror