使用 md-editor-v3 开发自定义 Markdown 编辑器组件

在日常项目中,开发一个功能丰富的 Markdown 编辑器对于提升用户体验和编辑效率非常重要。本文将介绍如何使用 md-editor-v3 来实现一个带有自定义工具栏和功能的 Markdown 编辑器组件。

Github: https://github.com/imzbf/md-editor-v3

一、项目结构和依赖

本示例使用 Vue 3 和 TypeScript 进行开发。核心依赖包括 md-editor-v3、@element-plus/icons-vue、@vavt/v3-extension 等。

bash 复制代码
yarn add md-editor-v3

使用现有的语言和主题等,如日语

bash 复制代码
yarn add @vavt/cm-extension

使用toolbar的现有组件,例如将内容导出为PDF

bash 复制代码
yarn add @vavt/v3-extension

二、代码实现详解

  1. 基础模板结构
    我们在模板中使用了 MdEditor 组件,并定义了一些自定义工具栏(toolbars中的自定义工具使用数字代替,从0开始)和弹窗。
javascript 复制代码
<template>
  <div w-full>
    <MdEditor
      v-model="editorText"
      :footers="['markdownTotal', '=', 0, 'scrollSwitch']"
      :theme="theme"
      :toolbars="toolbars"
      @on-save="onSave"
      @on-change="handleChange"
      @on-upload-img="onUploadImg"
    >
      <template #defToolbars>
        <ExportPDF :model-value="editorText" @on-progress="onProgress" @on-success="onSuccess" />
       <!--      自定义工具栏NormalToolbar,不需要可删除-->
        <NormalToolbar title="mark" @on-click="customHandler">
          <template #trigger>
            <el-icon>
              <VideoCameraFilled />
            </el-icon>
          </template>
        </NormalToolbar>
   <!--      自定义工具栏ModalToolbar,不需要可删除-->
        <ModalToolbar
          :visible="visible"
          height="200px"
          modal-title="测试"
          title="video"
          width="400px"
          @on-close="closeModal"
          @on-adjust="adjustModal"
          @on-click="openModal"
        >
          <template #trigger>
            <el-icon>
              <List />
            </el-icon>
          </template>
        </ModalToolbar>
      </template>
      <template #defFooters>
        <NormalFooterToolbar>{{ parseTime(new Date()) }}</NormalFooterToolbar>
      </template>
    </MdEditor>
  </div>
</template>
2. 脚本逻辑
在 <script setup> 部分,我们定义了组件的核心逻辑和交互方法。

typescript
复制代码
<script lang="ts" setup>
import { defineEmits, defineProps, ref, watch } from 'vue';
import { MdEditor, ModalToolbar, NormalFooterToolbar, NormalToolbar, Themes, ToolbarNames } from 'md-editor-v3';
import { upload } from '@/utils/request';
import { List, VideoCameraFilled } from '@element-plus/icons-vue';
import { ExportPDF } from '@vavt/v3-extension';
import modal from '@/plugins/modal';
import { parseTime } from '@/utils/ruoyi';

// 定义传入参数和事件
const emit = defineEmits(['update:modelValue']);
const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  }
});

// 工具栏配置
const toolbars = ref<ToolbarNames[]>([
  'revoke', 'next', '-', 'bold', 'underline', 'italic', 'strikeThrough', '-', 'title', 'sub', 
  'sup', 'quote', 'unorderedList', 'orderedList', 'task', 'codeRow', 'code', '-', 'link', 
  'image', 'table', 'mermaid', 'katex', '-', 0, 1, 2, '=', 'save', 'prettier', 'pageFullscreen', 
  'catalog', 'preview', 'previewOnly', 'htmlPreview', 'github'
]);

const editorText = ref(props.modelValue);
const visible = ref(false);

// 主题切换
const isDark = useDark({ storageKey: 'useDarkKey', valueDark: 'dark', valueLight: 'light' });
const theme = ref<Themes>(isDark.value ? 'dark' : 'light');

// 监听主题变化
watch(isDark, () => {
  theme.value = isDark.value ? 'dark' : 'light';
});

// 监听传入的值变化
watch(() => props.modelValue, (newValue) => {
  editorText.value = newValue;
});

// 更新父组件的 v-model
watch(editorText, (newValue) => {
  emit('update:modelValue', newValue);
});

// 保存操作
const onSave = (value: string) => {
  console.log(value);
};

// 文本改变操作
const handleChange = (text: string) => {
  editorText.value = text;
};

// 自定义工具栏方法
const customHandler = () => {
  alert('自定义菜单');
};

// 弹窗调整方法
const adjustModal = () => {
  visible.value = false;
};

// 弹窗开启方法
const openModal = () => {
  visible.value = true;
};

// 关闭弹窗
const closeModal = () => {
  visible.value = false;
};

// 导出 PDF 进度条
const onProgress = (progress: number) => {
  console.log(progress);
};

// PDF 导出成功
const onSuccess = () => {
  modal.msgSuccess('PDF导出成功!');
};

// 图片上传
const onUploadImg = async (files: File[], callback: (url: { alt: string; title: string; url: any }[]) => void) => {
  const res = await Promise.all(
    files.map((file) => {
      return new Promise((resolve, reject) => {
        const form = new FormData();
        form.append('file', file);
        upload(form)
          .then((res) => resolve(res))
          .catch((error) => reject(error));
      });
    })
  );
  callback(
    res.map((item: any) => ({
      url: item.data.url,
      alt: item.data.fileName,
      title: item.data.fileName
    }))
  );
};
</script>

三、功能实现

  1. 自定义工具栏和弹窗

    我们通过 和 组件定义了自定义工具栏和弹窗,用于扩展 Markdown 编辑器的功能,例如添加视频插入按钮和自定义弹窗。

  2. 图片上传

    实现了 onUploadImg 方法,支持异步上传图片,并通过回调返回图片的 URL、alt 和 title 属性。

  3. 主题切换

    使用了 useDark 实现编辑器的暗黑模式切换,用户体验更加灵活。

  4. PDF 导出

    使用 @vavt/v3-extension 提供的 ExportPDF 组件实现导出 PDF 功能,并监听导出进度和完成情况。

四、只读模式

javascript 复制代码
<template>
  <div class="yk-md-editor base-bg-box">
    <MdCatalog v-if="showCatalog" :editor-id="id" :scroll-element="scrollElement" :theme="theme" />
    <MdPreview :editor-id="id" :model-value="modelValue" :theme="theme" />
  </div>
</template>

<script setup>
import { MdCatalog, MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/preview.css';

const props = defineProps({
  modelValue: {
    type: String,
    default: ''
  },
  showCatalog: {
    type: Boolean,
    default: true
  }
});
// 是否暗黑模式
const isDark = useDark({
  storageKey: 'useDarkKey',
  valueDark: 'dark',
  valueLight: 'light'
});
const theme = ref(isDark.value ? 'dark' : 'light');
// 匹配菜单颜色
watch(isDark, () => {
  theme.value = isDark.value ? 'dark' : 'light';
});
const id = 'preview-only';
const scrollElement = document.documentElement;
</script>

<style lang="scss" scoped>
@import '@/assets/styles/variables.module.scss';

.yk-md-editor {
  border-radius: 5px;
  overflow: hidden;
  margin: 12px 0;
}

.md-editor {
  --md-bk-color: $base-bg-box;
}
</style>

五、总结

通过上述步骤,我们实现了一个功能强大、易于扩展的 Markdown 编辑器组件。希望这篇文章能帮助到你在项目中构建类似的编辑器。

转载自: https://web.yujky.cn/app/article/article-edit/index/1855647538840924161

具体效果可登录我的网站体验
https://web.yujky.cn/

租户:体验租户

用户名:cxks

密码: cxks123

相关推荐
陌上阳光3 小时前
vscode连接远程开发机报错
ide·vscode·编辑器
羊子雄起4 小时前
CKEditor前端样式和编辑器的样式不一致的问题
前端·编辑器
界面开发小八哥7 小时前
「Java EE开发指南」如何使用Visual JSF编辑器设计JSP?(一)
java·ide·java-ee·编辑器·myeclipse
Bio Coder9 小时前
vim 一次注释多行 的几种方法
linux·编辑器·vim·注释·快捷键·方法·取消注释
luckilyil19 小时前
前端—Cursor编辑器
前端·编辑器
一棵开花的树,枝芽无限靠近你1 天前
【PPTist】开源PPT编辑器初体验
编辑器·powerpoint
Random_index1 天前
#开发环境篇:vscode里面登录已同步设置的提示1怎么取消
ide·vscode·编辑器
赵闪闪1681 天前
Node.js 安装与环境配置详解:从入门到实战
node.js·编辑器·vim
风流野趣fly1 天前
好用的 IDEA 插件
java·编辑器·intellij-idea
一棵开花的树,枝芽无限靠近你1 天前
【element-tiptap】Tiptap编辑器核心概念----内容、扩展与词汇
前端·编辑器·element-tiptap