feat: Markdown编辑/预览组件(基于vditor)

This commit is contained in:
dap
2024-10-15 08:31:39 +08:00
parent 72ae9edd2c
commit c27acef777
5 changed files with 65 additions and 17 deletions

View File

@@ -6,6 +6,7 @@
- 全局表格加上id 方便进行缓存列排序的操作 - 全局表格加上id 方便进行缓存列排序的操作
- 支持菜单名称i18n - 支持菜单名称i18n
- 登录页 验证码登录 - 登录页 验证码登录
- Markdown编辑/预览组件(基于vditor)
**BUG FIXES** **BUG FIXES**

View File

@@ -1,15 +1,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { MarkdownEditor, Page } from '@vben/common-ui'; import { MarkdownPreviewer, Page } from '@vben/common-ui';
import { Skeleton } from 'ant-design-vue';
import changelog from '../../../../../../CHANGELOG.md?raw'; import changelog from '../../../../../../CHANGELOG.md?raw';
const content = ref(changelog); const content = ref(changelog);
const loading = ref(true);
</script> </script>
<template> <template>
<Page :auto-content-height="true"> <Page :auto-content-height="true">
<MarkdownEditor id="changelog" v-model:value="content" height="100%" /> <Skeleton v-show="loading" active />
<MarkdownPreviewer
v-model:value="content"
height="100%"
@mounted="loading = false"
/>
</Page> </Page>
</template> </template>

View File

@@ -21,15 +21,38 @@ const props = defineProps({
type: [String, Number], type: [String, Number],
default: 500, default: 500,
}, },
// 编辑器唯一ID 缓存使用 /**
* 编辑模式。默认值: 'wysiwyg'
* wysiwyg: 所见即所得
* ir: 即时渲染
* sv: 分屏预览
*/
mode: {
type: String as PropType<'ir' | 'sv' | 'wysiwyg'>,
default: 'wysiwyg',
},
// 编辑器唯一ID 缓存使用 可记录上次输入
id: { id: {
type: String, type: String,
required: true, required: false,
default: '',
validator(value, props) {
if (!value && props.enableCache) {
console.warn('The id is required when enableCache is true');
return false;
}
return true;
},
}, },
enableCache: { enableCache: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
// 禁用编辑器
disabled: {
type: Boolean,
default: false,
},
// 其他配置项 // 其他配置项
options: { options: {
type: Object as PropType<IOptions>, type: Object as PropType<IOptions>,
@@ -60,8 +83,19 @@ const content = defineModel('value', {
default: '', default: '',
}); });
// 监听禁用
function changeDisabled(disabled: boolean) {
if (disabled) {
vditorInstance.value?.disabled();
} else {
vditorInstance.value?.enable();
}
}
watch(() => props.disabled, changeDisabled);
onMounted(() => { onMounted(() => {
vditorInstance.value = new Vditor(vditorRef.value!, { vditorInstance.value = new Vditor(vditorRef.value!, {
mode: props.mode,
value: content.value, value: content.value,
height: props.height, height: props.height,
lang: locale.value.replace('-', '_') as any, lang: locale.value.replace('-', '_') as any,
@@ -76,6 +110,8 @@ onMounted(() => {
}, },
// 加载完成的事件 // 加载完成的事件
after() { after() {
// 需要初始化就禁用的情况
changeDisabled(props.disabled);
emit('mounted'); emit('mounted');
}, },
...props.options, ...props.options,

View File

@@ -1,2 +1,2 @@
export { default as MarkdownEditor } from './editor.vue'; export { default as MarkdownEditor } from './editor.vue';
export { default as MarkdownPreview } from './preview.vue'; export { default as MarkdownPreviewer } from './preview.vue';

View File

@@ -17,18 +17,10 @@ import 'vditor/dist/index.css';
const props = defineProps({ const props = defineProps({
// 编辑器高度 // 编辑器高度
height: { height: {
type: Number, // string或者number类型
type: [String, Number],
default: 500, default: 500,
}, },
// 编辑器唯一ID 缓存使用
id: {
type: String,
required: true,
},
enableCache: {
type: Boolean,
default: false,
},
// 其他配置项 // 其他配置项
options: { options: {
type: Object as PropType<IOptions>, type: Object as PropType<IOptions>,
@@ -65,10 +57,11 @@ onMounted(() => {
height: props.height, height: props.height,
lang: locale.value.replace('-', '_') as any, lang: locale.value.replace('-', '_') as any,
cache: { cache: {
enable: props.enableCache, enable: false,
id: props.id,
}, },
theme: isDark.value ? 'dark' : 'classic', theme: isDark.value ? 'dark' : 'classic',
// 预览(只读模式) 不显示工具栏
toolbar: [],
// 手动响应式 // 手动响应式
input(value) { input(value) {
content.value = value; content.value = value;
@@ -76,6 +69,8 @@ onMounted(() => {
// 加载完成的事件 // 加载完成的事件
after() { after() {
emit('mounted'); emit('mounted');
// 禁用编辑器
vditorInstance.value?.disabled();
}, },
...props.options, ...props.options,
}); });
@@ -90,3 +85,10 @@ onBeforeUnmount(() => {
<template> <template>
<div ref="vditorRef"></div> <div ref="vditorRef"></div>
</template> </template>
<style>
.vditor-ir pre.vditor-reset[contenteditable='false'] {
cursor: unset;
opacity: 1;
}
</style>