4 changed files with 211 additions and 0 deletions
@ -0,0 +1,173 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import type { PropType } from 'vue'; |
||||
|
|
||||
|
import { computed, nextTick, onBeforeUnmount, onMounted, watch } from 'vue'; |
||||
|
|
||||
|
import { preferences } from '@vben/preferences'; |
||||
|
|
||||
|
import Vditor from 'vditor'; |
||||
|
|
||||
|
import { toolbar as defaultToolbar } from './vditor'; |
||||
|
|
||||
|
import 'vditor/dist/index.css'; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
cacheKey: { |
||||
|
default: undefined, |
||||
|
type: String, |
||||
|
}, |
||||
|
height: { |
||||
|
default: 500, |
||||
|
type: Number, |
||||
|
}, |
||||
|
mode: { |
||||
|
default: 'wysiwyg', |
||||
|
type: String as PropType<'ir' | 'sv' | 'wysiwyg'>, |
||||
|
}, |
||||
|
modelValue: { |
||||
|
default: '', |
||||
|
type: String, |
||||
|
}, |
||||
|
readonly: { |
||||
|
default: false, |
||||
|
type: Boolean, |
||||
|
}, |
||||
|
tab: { |
||||
|
default: '\t', |
||||
|
type: String, |
||||
|
}, |
||||
|
toolbar: { |
||||
|
default: defaultToolbar, |
||||
|
type: Array as PropType<string[]>, |
||||
|
}, |
||||
|
typewriterMode: { |
||||
|
default: false, |
||||
|
type: Boolean, |
||||
|
}, |
||||
|
value: { |
||||
|
default: '', |
||||
|
type: String, |
||||
|
}, |
||||
|
}); |
||||
|
const emits = defineEmits<{ |
||||
|
(event: 'change', content: any): void; |
||||
|
(event: 'update:modelValue', content: any): void; |
||||
|
}>(); |
||||
|
let editor: Vditor; |
||||
|
|
||||
|
const langName = computed(() => { |
||||
|
switch (preferences.app.locale) { |
||||
|
case 'zh-CN': { |
||||
|
return 'zh_CN'; |
||||
|
} |
||||
|
default: { |
||||
|
return 'en_US'; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
const skinName = computed(() => { |
||||
|
return preferences.theme.mode === 'light' ? 'classic' : 'dark'; |
||||
|
}); |
||||
|
|
||||
|
function initEditor() { |
||||
|
editor = new Vditor('vditor', { |
||||
|
cache: { |
||||
|
enable: !!props.cacheKey, |
||||
|
id: props.cacheKey, |
||||
|
}, |
||||
|
counter: { |
||||
|
enable: true, |
||||
|
type: 'markdown', |
||||
|
}, |
||||
|
height: props.height, |
||||
|
input(value) { |
||||
|
emits('update:modelValue', value); |
||||
|
emits('change', value); |
||||
|
}, |
||||
|
lang: langName.value, |
||||
|
mode: props.mode, |
||||
|
preview: { |
||||
|
delay: 0, |
||||
|
hljs: { |
||||
|
lineNumber: true, |
||||
|
style: 'monokai', |
||||
|
}, |
||||
|
}, |
||||
|
tab: props.tab, |
||||
|
theme: skinName.value, |
||||
|
toolbar: props.toolbar, |
||||
|
toolbarConfig: { |
||||
|
pin: true, |
||||
|
}, |
||||
|
typewriterMode: props.typewriterMode, |
||||
|
value: props.value, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function resetEditor() { |
||||
|
nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
initEditor(); |
||||
|
}, 30); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function destory() { |
||||
|
if (editor !== null) { |
||||
|
editor?.destroy(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function setValue(editor: Vditor, val?: string, prevVal?: string) { |
||||
|
if ( |
||||
|
editor && |
||||
|
typeof val === 'string' && |
||||
|
val !== prevVal && |
||||
|
val !== editor.getValue() |
||||
|
) { |
||||
|
editor.setValue(val); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
watch( |
||||
|
() => props.modelValue, |
||||
|
(val?: string, prevVal?: string) => { |
||||
|
setValue(editor, val, prevVal); |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
watch( |
||||
|
() => props.value, |
||||
|
(val?: string, prevVal?: string) => { |
||||
|
setValue(editor, val, prevVal); |
||||
|
}, |
||||
|
{ |
||||
|
immediate: true, |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
watch( |
||||
|
() => props.toolbar, |
||||
|
() => { |
||||
|
if (editor) { |
||||
|
destory(); |
||||
|
setTimeout(resetEditor, 100); |
||||
|
} |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
resetEditor(); |
||||
|
}); |
||||
|
|
||||
|
onBeforeUnmount(() => { |
||||
|
destory(); |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div id="vditor"></div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped></style> |
||||
@ -0,0 +1 @@ |
|||||
|
export { default as MarkdownEditor } from './Editor.vue'; |
||||
@ -0,0 +1,32 @@ |
|||||
|
export const toolbar = [ |
||||
|
'emoji', |
||||
|
'headings', |
||||
|
'bold', |
||||
|
'italic', |
||||
|
'strike', |
||||
|
'link', |
||||
|
'|', |
||||
|
'list', |
||||
|
'ordered-list', |
||||
|
'check', |
||||
|
'outdent', |
||||
|
'indent', |
||||
|
'|', |
||||
|
'quote', |
||||
|
'line', |
||||
|
'code', |
||||
|
'inline-code', |
||||
|
'insert-before', |
||||
|
'insert-after', |
||||
|
'|', |
||||
|
// 'record',
|
||||
|
'table', |
||||
|
'|', |
||||
|
'undo', |
||||
|
'redo', |
||||
|
'|', |
||||
|
'edit-mode', |
||||
|
// 'content-theme',
|
||||
|
'code-theme', |
||||
|
'export', |
||||
|
]; |
||||
Loading…
Reference in new issue