Skip to content

Vue API

@frame-flex/vue 包提供 Vue 3 组件和 Composables。

RichTextEditor 组件

主编辑器组件,使用 TipTap 和 ProseMirror 构建。

Props

typescript
interface Props {
  // v-model 绑定的内容
  modelValue?: JSONContent;

  // 是否可编辑
  editable?: boolean;
}

modelValue

  • 类型: JSONContent
  • 默认值: undefined
  • 描述: 编辑器内容(TipTap JSON 格式)

示例:

vue
<script setup>
const content = ref({
  type: 'doc',
  content: [
    {
      type: 'paragraph',
      content: [{ type: 'text', text: 'Hello' }]
    }
  ]
});
</script>

<template>
  <RichTextEditor v-model="content" />
</template>

editable

  • 类型: boolean
  • 默认值: true
  • 描述: 是否允许编辑

示例:

vue
<template>
  <RichTextEditor v-model="content" :editable="false" />
</template>

Events

update:modelValue

当内容更新时触发。

typescript
(content: JSONContent) => void

示例:

vue
<template>
  <RichTextEditor
    v-model="content"
    @update:modelValue="handleUpdate"
  />
</template>

<script setup>
function handleUpdate(newContent) {
  console.log('内容更新:', newContent);
}
</script>

update

内容更新事件(同 update:modelValue)。

typescript
(content: JSONContent) => void

Slots

RichTextEditor 组件当前不提供插槽,但你可以通过 useEditor 完全自定义编辑器。

useEditor

编辑器管理 Composable。

类型定义

typescript
interface UseEditorReturn {
  // 编辑器实例(shallowRef)
  editor: ShallowRef<Editor | null>;

  // 创建编辑器
  createEditor: (options?: EditorOptions) => Editor;

  // 销毁编辑器
  destroyEditor: () => void;
}

function useEditor(): UseEditorReturn;

使用方法

vue
<script setup lang="ts">
import { onMounted, onBeforeUnmount } from 'vue';
import { useEditor } from '@frame-flex/vue';

const { editor, createEditor, destroyEditor } = useEditor();

onMounted(() => {
  createEditor({
    content: '<p>Hello World</p>',
    editable: true,
    onUpdate: (content) => {
      console.log('更新:', content);
    }
  });
});

onBeforeUnmount(() => {
  destroyEditor();
});

// 使用编辑器实例
function getHTML() {
  return editor.value?.getHTML();
}
</script>

EditorOptions

typescript
interface EditorOptions {
  // 初始内容
  content?: string | JSONContent;

  // 是否可编辑
  editable?: boolean;

  // 自动聚焦
  autofocus?: boolean | 'start' | 'end' | 'all';

  // 内容更新回调
  onUpdate?: (content: JSONContent) => void;

  // 选区更新回调
  onSelectionUpdate?: () => void;

  // 焦点回调
  onFocus?: () => void;

  // 失焦回调
  onBlur?: () => void;
}

扩展

VueMathExtension

Vue 数学公式扩展(带 NodeView)。

typescript
import { VueMathInline, VueMathBlock } from '@frame-flex/vue';

特性:

  • 行内公式和块级公式
  • KaTeX 渲染
  • Vue 组件编辑界面

使用:

vue
<script setup>
import { useEditor } from '@frame-flex/vue';
import { VueMathInline, VueMathBlock } from '@frame-flex/vue';

const { createEditor } = useEditor();

onMounted(() => {
  createEditor({
    extensions: [VueMathInline, VueMathBlock]
  });
});
</script>

VueCodeBlockExtension

Vue 代码块扩展(带 NodeView)。

typescript
import { VueCodeBlockExtension } from '@frame-flex/vue';

特性:

  • 多语言支持
  • 语法高亮(lowlight)
  • Vue 组件编辑界面
  • 语言选择器

NodeView 组件

MathComponent

数学公式 NodeView 组件。

Props:

  • 继承自 nodeViewProps

功能:

  • KaTeX 渲染
  • 双击编辑
  • LaTeX 输入

CodeBlockComponent

代码块 NodeView 组件。

Props:

  • 继承自 nodeViewProps

功能:

  • 语法高亮
  • 语言选择
  • 复制代码按钮

MediaComponents

媒体文件 NodeView 组件集。

  • ImageComponent: 图片组件
  • VideoComponent: 视频组件
  • AudioComponent: 音频组件

类型定义

JSONContent

typescript
interface JSONContent {
  type: string;
  attrs?: Record<string, any>;
  content?: JSONContent[];
  marks?: Array<{
    type: string;
    attrs?: Record<string, any>;
  }>;
  text?: string;
}

Editor

typescript
import type { Editor } from '@tiptap/core';

// TipTap Editor 实例
const editor: Editor | null = useEditor().editor.value;

样式

导入样式

typescript
import '@frame-flex/vue/styles';

或在 CSS 中:

css
@import '@frame-flex/vue/styles';

自定义样式

使用 CSS 变量覆盖默认样式:

css
:root {
  --editor-bg: #ffffff;
  --editor-text: #1a202c;
  --editor-border: #e2e8f0;
}

完整示例

vue
<template>
  <div class="app">
    <RichTextEditor
      v-model="content"
      :editable="editable"
      @update="handleUpdate"
    />

    <div class="actions">
      <button @click="toggleEditable">
        {{ editable ? '锁定' : '解锁' }}
      </button>
      <button @click="getContent">获取内容</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { RichTextEditor, useEditor } from '@frame-flex/vue';
import type { JSONContent } from '@frame-flex/core';
import '@frame-flex/vue/styles';

const content = ref<JSONContent>({
  type: 'doc',
  content: []
});

const editable = ref(true);
const { editor } = useEditor();

function handleUpdate(newContent: JSONContent) {
  console.log('内容更新:', newContent);
}

function toggleEditable() {
  editable.value = !editable.value;
}

function getContent() {
  if (editor.value) {
    console.log('HTML:', editor.value.getHTML());
    console.log('JSON:', editor.value.getJSON());
    console.log('Text:', editor.value.getText());
  }
}
</script>

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

.actions {
  margin-top: 20px;
  display: flex;
  gap: 10px;
}

button {
  padding: 10px 20px;
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
}
</style>

下一步

Released under the MIT License.