初探tiptap,实现一个结构化报告模板

最近在做医疗结构化报告的功能,实现检查结果的编辑和部分项目的自定义,调研的了一下使用tiptap来实现。先看下官方简介:Tiptap 是一个无头富文本编辑器框架,它可以让你创建一个完全符合你和你的客户需求的自定义编辑器。它建立在 ProseMirror 的基础之上,ProseMirror 是一个久经考验的用于在网络上构建富文本编辑器的库。Tiptap 严重依赖于事件、命令和扩展,为构建编辑器提供了灵活而强大的 API。话不多说,show code。

安装依赖

由于项目使用vue,需要安装tiptap vue的依赖

bash 复制代码
npm install @tiptap/vue-3 @tiptap/pm @tiptap/starter-kit

其中 @tiptap/pm是ProseMirror,@tiptap/starter-kit是官方提供的一个基础扩展组合包,它帮你一次性引入了一组最常用的 Node 和 Mark(加粗、斜体、标题、列表等),这样你不用一个个手动去安装和注册。我们是做个demo,就先全量引入了,后续可以自定义。

集成tiptap

xml 复制代码
// Editor.vue
<template>
  <editor-content :editor="editor" />
</template>

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

  const editor = useEditor({
    content: "<p>检查报告单</p>",
    extensions: [StarterKit],
  })
</script>

这里我们使用了useEditor这个hook,content是要显示的内容,extensions是tiptap的拓展,返回的editor我们绑定到editor-content组件上,就能显示出内容了,可以像其他富文本编辑器一样在页面上编辑内容。

添加自定义组件

在Tiptap里,"添加自定义组件"其实就是自定义 Node 或 Mark 扩展,然后通过 Vue/React 的渲染桥接把它变成可视化组件。

  1. 自定义Node

我们想要实现一个select组件,实现疾病风险等级的选择,首先需要定义node,们创建一个 CustomSelect 节点扩展

typescript 复制代码
// SelectNode.ts

import { Node, mergeAttributes } from '@tiptap/core';
import { VueNodeViewRenderer } from '@tiptap/vue-3';
import SelectNodeView from './components/CustomSelect.vue';

export default Node.create({
  name: 'customSelect',

  group: 'inline',
  inline: true,
  atom: true,

  addAttributes() {
    return {
      value: {
        default: '',
        parseHTML: (element) => element.getAttribute('data-value') || '',
        renderHTML: (attributes) => {
          return { 'data-value': attributes.value };
        },
      },
    };
  },

  parseHTML() {
    return [{ tag: 'custom-select' }];
  },

  renderHTML({ HTMLAttributes }) {
    return ['custom-select', mergeAttributes(HTMLAttributes)];
  },

  addNodeView() {
    return VueNodeViewRenderer(SelectNodeView);
  },
});

关键点:

  • 自定义拓展设置name,在通过editor.getJSON获取内容的json是,为node的type值
  • 在组件的属性(attrs)里存储当前选中的值,parseHTML和renderTML解析和显示自定义节点到html上
  • VueNodeViewRenderer是tiptap提供的vue组件渲染器,也有react组件的渲染器
  1. 创建组件

实现select组件

vue 复制代码
<template>
  <node-view-wrapper>
    <select :value="node.attrs.value" @change="onChange">
      <option value="">请选择</option>
      <option v-for="op in options" :key="op.value" :value="op.value">
        {{ op.label }}
      </option>
    </select>
  </node-view-wrapper>
</template>

<script setup lang="ts">
import { NodeViewWrapper } from '@tiptap/vue-3';
import { useSelections } from '../useStore';
const props = defineProps();

const options = useSelections();

function onChange(event: Event) {
  const target = event.target as HTMLSelectElement;
  props.updateAttributes({ value: target.value });
}
</script>

实现useSelections,选项可自定义返回,这里我们模拟1s后返回选项

ts 复制代码
import { ref, onMounted } from 'vue';
export function useSelections() {
  const options = ref<{ label: string; value: string }[]>([]);

  onMounted(() => {
    setTimeout(() => {
      options.value = [
        { label: '重度', value: 'A' },
        { label: '中度', value: 'B' },
        { label: '轻度', value: 'C' },
      ];
    }, 1000);
  });

  return options;
}

关键点:

  • 组件模板需要NodeViewWrapper包裹才能正常显示
  • props里有node,和updateAttributes这两个属性,可通过node.attr取值,updateAttributes更新值
  1. 在编辑器中注册

我们把自定义节点注册到editr的拓展中,并修改content

js 复制代码
editor = useEditor({
  content: '<p>检查报告单</p><p>风险等级:<custom-select></custom-select></p>',
  extensions: [StarterKit, CustomSelect],
});

这时候我们的自定义节点就会显示出来,可以自定义选项和选择结果。最终输出结果可以通过editor.getJSON或者editor.getHTML得到json类型的数据或者html结构,保存到数据库实现后续的渲染和编辑。

以上是使用tiptap实现简单的结构化报告模板的渲染和编辑,tiptap提供了丰富的拓展和插件,当我们有类似需求的时候可以考虑使用tiptap。

相关推荐
苏格拉没有底了37 分钟前
由频繁创建3D火焰造成的内存泄漏问题
前端
阿彬爱学习39 分钟前
大模型在垂直场景的创新应用:搜索、推荐、营销与客服新玩法
前端·javascript·easyui
橙序员小站1 小时前
通过trae开发你的第一个Chrome扩展插件
前端·javascript·后端
Lazy_zheng1 小时前
一文掌握:JavaScript 数组常用方法的手写实现
前端·javascript·面试
是晓晓吖1 小时前
关于Chrome Extension option的一些小事
前端·chrome
MrSkye1 小时前
🔥从菜鸟到高手:彻底搞懂 JavaScript 事件循环只需这一篇(下)
前端·javascript·面试
方佑1 小时前
✨ Nuxt 混合渲染实践: MemOS前端体验深度优化指南
前端
爱编程的喵1 小时前
React 19 + Vite 6 构建现代化旅行应用智旅(1)
前端·react.js
l1t1 小时前
使用流式函数解决v语言zstd程序解压缩失败问题
前端·压缩·v语言·zstd
小离a_a1 小时前
el-tree方法的整理
前端·vue.js·elementui