Files
docs/versioned_docs/version-2.20/developer-guide/plugin/extension-points/ui/editor-create.md
guqing 717ee402f4 refactor: update permission control docs for plugin (#443)
### What this PR does?
重构权限控制部分文档并调整相应结构

```release-note
None
```
2024-10-25 08:01:56 +00:00

4.6 KiB
Raw Blame History

title, description
title description
编辑器集成 通过实现扩展点为文章提供新的编辑器 - editor:create

此扩展点可以为文章提供新的独立编辑器。

编辑器集成

定义方式

export default definePlugin({
  extensionPoints: {
    "editor:create": (): EditorProvider[] | Promise<EditorProvider[]> => {
      return [
        {
          name: "foo",
          displayName: "foo",
          logo: "/plugins/plugin-foo/assets/logo.png",
          component: FooComponent,
          rawType: "markdown",
        },
      ];
    },
  },
});
export interface EditorProvider {
  name: string;
  displayName: string;
  logo?: string;
  component: Component;
  rawType: string;
}

其中,component 可以是组件对象或组件名称,且此组件有以下实现要求:

  1. 组件包含以下 props
    1. title:string:用于接收标题。
    2. raw:string:用于接收原始内容。
    3. content:string:用于接收渲染后的内容。
    4. uploadImage?: (file: File) => Promise<Attachment>:用于上传图片,在编辑器内部获取到 File 之后直接调用此方法即可得到上传后的附件信息。
  2. 组件包含以下 emit 事件:
    1. update:title:用于更新标题。
    2. update:raw:用于更新原始内容。
    3. update:content:用于更新渲染后的内容。
    4. update:发送更新事件。

示例

此示例将实现一个简单的 Markdown 编辑器。

import { definePlugin } from "@halo-dev/console-shared";
import { markRaw } from "vue";
import MarkdownEditor from "./components/markdown-editor.vue";

export default definePlugin({
  extensionPoints: {
    "editor:create": () => {
      return [
        {
          name: "markdown",
          displayName: "Markdown 编辑器",
          component: markRaw(MarkdownEditor),
          rawType: "markdown",
          logo: "/plugins/markdown-editor/assets/logo.png",
        },
      ];
    },
  },
});
<script setup lang="ts">
import { marked } from "marked";
import { debounce } from "lodash-es";
import { ref, computed, onMounted } from "vue";
import type { Attachment } from "@halo-dev/api-client";

const props = withDefaults(
  defineProps<{
    raw: string;
    content: string;
    uploadImage?: (file: File) => Promise<Attachment>;
  }>(),
  {
    raw: "",
    content: "",
    uploadImage: undefined,
  }
);

const emit = defineEmits<{
  (event: "update:raw", value: string): void;
  (event: "update:content", value: string): void;
  (event: "update", value: string): void;
}>();

const output = computed(() => marked(props.raw));

const update = debounce((e) => {
  emit("update:raw", e.target.value);
  emit("update:content", marked(e.target.value));

  if (e.target.value !== props.raw) {
    emit("update", e.target.value);
  }
}, 100);

const textareaRef = ref();

onMounted(() => {
  textareaRef.value.addEventListener("paste", (e) => {
    if (e.clipboardData && e.clipboardData.items) {
      const items = e.clipboardData.items;
      for (let i = 0; i < items.length; i++) {
        if (items[i].type.indexOf("image") !== -1) {
          const file = items[i].getAsFile();
          props.uploadImage?.(file).then((attachment: Attachment) => {
            emit(
              "update:raw",
              props.raw +
                `![${attachment.spec.displayName}](${attachment.status?.permalink})`
            );
          });
        }
      }
    }
  });
});
</script>

<template>
  <div class="editor">
    <textarea ref="textareaRef" class="input" :value="raw" @input="update"></textarea>
    <div class="output" v-html="output"></div>
  </div>
</template>

<style>
body {
  margin: 0;
}

.editor {
  height: 100vh;
  display: flex;
}

.input,
.output {
  overflow: auto;
  width: 50%;
  height: 100%;
  box-sizing: border-box;
  padding: 0 20px;
}

.input {
  border: none;
  border-right: 1px solid #ccc;
  resize: none;
  outline: none;
  background-color: #f6f6f6;
  font-size: 14px;
  font-family: 'Monaco', courier, monospace;
  padding: 20px;
}

code {
  color: #f66;
}
</style>

来源:https://vuejs.org/examples/#markdown

实现案例