https://gitee.com/duqingbing/ts-v3-package/tree/ts-v3-md-editor
vite + ts + vue3 二次封装 md-editor-v3 出来的 MdEditor 编辑器
- @qingbing/ts-v3-utils: ^2.0.15"
- md-editor-v3: ^4.12.2"
- vue: ^3.4.21"
# yarn 安装
yarn add @qingbing/ts-v3-md-editor
# npm 安装
npm i @qingbing/ts-v3-md-editor
属性名 | 类型 | 是否必需 | 默认值 | 意义 |
---|---|---|---|---|
conf | Object | 否 | { theme: 'light', showCodeRowNumber: true } | 预览配置 |
- conf 内部属性说明(主要参考
md-editor-v3
)
属性名 | 类型 | 默认值 | 意义 |
---|---|---|---|
modelValue | string | '' | 编辑内容,可使用 v-model="value" 实现双向绑定 |
theme | 'light' | 'dark' | 'light' |
class | string | '' | 编辑器最外层样式 |
language | string | 'zh-CN' | 内置中英文('zh-CN','en-US') |
previewTheme | TMdEditorPreviewTheme | 'default' | 预览内容主题 |
editorId | string | 'md-editor-v3' | 编辑器唯一标识,非必须,防止产生服务端与客户端渲染内容不一致错误提示,以及单页面多编辑器时做区别 |
showCodeRowNumber | boolean | false | 代码块显示行号 |
style | string \ CSSProperties | '' | 编辑器内联样式 |
noMermaid | boolean | false | 不希望使用图表展示内容,可以设置关闭 |
noKatex | boolean | false | 不希望使用数学公式展示内容,可以设置关闭 |
codeTheme | TMdEditorCodeTheme | 'atom' | 代码块高亮样式名称 |
noIconfont | boolean | true | 不插入 iconfont 链接,可以下载到本地自行引入 |
codeStyleReverse | boolean | true | 在主题下的 light 模式上使用暗色系的代码风格 |
codeStyleReverseList | Array | ['default', 'mk-cute'] | 需要自动调整的预览主题 |
noHighlight | boolean | false | 不高亮代码,也不会加载相应的扩展库 |
noImgZoomIn | boolean | false | 关闭编辑器默认的放大功能(^4.4.0) |
customIcon | CustomIcon | {} | 自定义的图标 |
sanitizeMermaid | (h: string) => Promise | (h: string) => Promise.resolve(h) | 转换生成的 mermaid 代码 |
mdHeadingId | (text: string, level: number, index: number) => string | (text) => text | 构造标题ID的生成方式 |
formatCopiedText | (text: string) => string | (text) => text | 格式化复制代码 |
事件名 | 类型 | 意义 |
---|---|---|
onHtmlChanged | (h: string) => void | html 变化回调事件,用于获取预览 html 代码 |
onGetCatalog | (list: HeadList[]) => void | 动态获取markdown目录 |
属性名 | 类型 |
---|---|
- | 无 |
属性名 | 类型 | 是否必需 | 默认值 | 意义 |
---|---|---|---|---|
conf | Object | 否 | { mode: "simple", theme: 'light', showCodeRowNumber: true } | 编辑器配置 |
- conf 内部属性说明(主要参考
md-editor-v3
), 拥有 MdPreview - conf 的所有属性
属性名 | 类型 | 默认值 | 说明 |
---|---|---|---|
mode | string | 'simple' | 组件定义的编辑器模式,主要控制 toolbar |
pageFullscreen | boolean | false | 页面内全屏 |
preview | boolean | true | 是否显示预览 |
htmlPreview | boolean | false | 是否显示 html 预览。当设置为true时,需要将preview设置为false |
toolbars | TMdEditorToolbarItem[] | [all] | 选择性展示工具栏 |
toolbarsExclude | TMdEditorToolbarItem[] | [] | 选择性不展示工具栏 |
noPrettier | boolean | false | 是否启用 prettier 优化 md 内容 |
tabWidth | number | 2 | 编辑器一个 TAB 键等于空格数 |
tableShape | [number, number] | [6, 4] | 标题栏添加表格时,预设待选表格大小,第一个代表最大列数,第二个代表最大行数 |
placeholder | string | '' | 啊这-_-! |
footers | Array<'markdownTotal' | '=' | 'scrollSwitch' |
scrollAuto | boolean | true | 同步滚动状态 |
noUploadImg | boolean | false | 工具栏不显示上传图片入口 |
autoFocus | boolean | false | 原生属性,文本区域自动获得焦点 |
disabled | boolean | false | 原生属性,禁用文本区域 |
readOnly | boolean | false | 原生属性,文本区域为只读 |
maxLength | number | `` | 原生属性,文本区域允许的最大字符数 |
autoDetectCode | boolean | false | 是否启用自动识别粘贴代码类别,目前仅支持从vscode复制的内容 |
showToolbarName | boolean | false | 是否在工具栏下面显示对应的文字名称 |
inputBoxWitdh | string | 50% | 输入框默认的宽度x |
inputBoxWidth | string | 50% | 输入框默认的宽度x, 纠正组件单词错误 |
事件名 | 类型 | 意义 |
---|---|---|
onChange | (v: string) => void | 内容变化事件(当前与textarea的oninput事件绑定,每输入一个单字即会触发) |
onSave | (v: string, h: Promise) => void | 保存事件,快捷键与保存按钮均会触发 |
onUploadImg | (files:Array,callBack:(urls: | string[] |
onError | (err: { name: 'Cropper' | 'fullscreen' |
onBlur | (event: FocusEvent) => void | 输入框失去焦点时触发事件 |
onFocus | (event: FocusEvent) => void | 输入框获得焦点时触发事件 |
onInput | (event: Event) => void | 输入框键入内容事件 |
onDrop | (event: DragEvent) => void | 拖放内容事件 |
onInputBoxWitdhChange | (width: string) => void | 调整输入框宽度事件 |
onInputBoxWidthChange | (width: string) => void | 调整输入框宽度事件, 纠正组件单词错误 |
属性名 | 类型 |
---|---|
- | 无 |
- 一旦注册,
MdEditor
和MdPreview
作为组件全局通用 - 使用方法参考 3.2 模板组件使用, 去掉组件导入的语句即可
// main.ts
import "@qingbing/ts-v3-md-editor/dist/style.css"
import { MdEditorPlugin } from '@qingbing/ts-v3-md-editor'
app.use(MdEditorPlugin)
<template>
<h1>Page</h1>
<h2> editor</h2>
<MdEditor v-model="text" :conf="mdEditorConf" />
<MdEditor v-model="content" :conf="{ mode: 'mini' }" />
<MdEditor v-model="content" :conf="{ mode: 'simple' }" />
<MdEditor v-model="content" :conf="{ mode: 'normal' }" />
<MdEditor v-model="content" />
<hr>
<MdPreview v-model="content" />
<MdPreview v-model="content" :conf="mdPreviewConf" />
</template>
<script setup lang="ts">
import 'md-editor-v3/lib/style.css'; // 编辑器样式
import 'md-editor-v3/lib/preview.css'; // 编辑器预览样式
import type { TMdEditorInnerError, TMdEditorProps, TMdEditorUploadImgCallBack, TMdPreviewProps } from "@qingbing/ts-v3-md-editor";
import { ref } from 'vue';
import { MdEditor, MdPreview } from "@qingbing/ts-v3-md-editor"; // 如果全局注册,这句直接删除
const mdEditorConf: TMdEditorProps = {
mode: 'normal',
// 内容变化事件(当前与textarea的oninput事件绑定,每输入一个单字即会触发)
onChange: (v: string) => {
console.log(v);
},
// 保存事件,快捷键与保存按钮均会触发
onSave: (v: string, h: Promise<string>) => {
console.log(v);
h.then((html) => {
console.log(html);
});
},
// 上传图片事件,弹窗会等待上传结果,务必将上传后的 urls 作为 callback 入参回传
onUploadImg: async (files: Array<File>, callback: TMdEditorUploadImgCallBack) => {
const res = await Promise.all(
files.map((file) => {
return new Promise((rev, rej) => {
rev([
{
data: {
url: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
}
},
{
data: {
url: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
}
},
])
// const form = new FormData();
// form.append('file', file);
// axios
// .post('/api/img/upload', form, {
// headers: {
// 'Content-Type': 'multipart/form-data'
// }
// })
// .then((res) => rev(res))
// .catch((error) => rej(error));
});
})
);
// 方式一
// callback(res.map((item) => item.data.url));
callback(res.map((item) => 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'));
// 方式二
// callback(
// res.map((item: any) => ({
// url: item.data.url,
// alt: 'alt',
// title: 'title'
// }))
// );
},
// 捕获执行错误事件,目前支持Cropper、fullscreen、prettier实例未加载完成操作,以及输入内容超出限制长度的错误
onError: (err: TMdEditorInnerError) => {
alert(err.message);
},
// 输入框失去焦点时触发事件
onBlur: (event: FocusEvent) => {
console.log('onBlur', event);
},
// 输入框获得焦点时触发事件
onFocus: (event: FocusEvent) => { },
// 输入框键入内容事件,
onInput: (event: Event) => { },
// 拖放内容事件
onDrop: (event: DragEvent) => {
event.stopPropagation();
console.log('ee', event.dataTransfer?.files[0]);
},
// 调整输入框宽度事件
onInputBoxWitdhChange: (width: string) => { },
}
const mdPreviewConf: TMdPreviewProps = {
theme: 'dark',
showCodeRowNumber: false
}
const content = ref("# title\n## sub title");
const text = ref("# Hello Editor\n```php\n$a = 5;\n$b = 6;\n$c = $a + $b\n```");
</script>