|
@@ -0,0 +1,113 @@
|
|
|
+<template>
|
|
|
+ <div class="rich-editor">
|
|
|
+ <QuillEditor
|
|
|
+ ref="editorRef"
|
|
|
+ v-model:content="content"
|
|
|
+ theme="snow"
|
|
|
+ content-type="html"
|
|
|
+ :placeholder="placeholder"
|
|
|
+ :toolbar="toolbar"
|
|
|
+ @update:content="contentChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+ import { ref, watch } from 'vue';
|
|
|
+ import { QuillEditor } from '@vueup/vue-quill';
|
|
|
+ import '@vueup/vue-quill/dist/vue-quill.snow.css';
|
|
|
+ // import ImageResize from 'quill-image-resize-module';
|
|
|
+
|
|
|
+ import useParser from './useParser';
|
|
|
+ import useRender from './useRender';
|
|
|
+ import { RichTextJSON } from './types';
|
|
|
+
|
|
|
+ defineOptions({
|
|
|
+ name: 'RichEditor',
|
|
|
+ });
|
|
|
+
|
|
|
+ const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ modelValue: string | RichTextJSON | null;
|
|
|
+ placeholder?: string;
|
|
|
+ autoEmit?: boolean;
|
|
|
+ }>(),
|
|
|
+ {
|
|
|
+ placeholder: '请输入',
|
|
|
+ autoEmit: false,
|
|
|
+ }
|
|
|
+ );
|
|
|
+ const emit = defineEmits(['update:modelValue', 'change']);
|
|
|
+
|
|
|
+ const toolbar = [
|
|
|
+ [
|
|
|
+ 'bold',
|
|
|
+ 'italic',
|
|
|
+ 'underline',
|
|
|
+ { script: 'sub' },
|
|
|
+ { script: 'super' },
|
|
|
+ 'image',
|
|
|
+ ],
|
|
|
+ ['clean'],
|
|
|
+ ];
|
|
|
+
|
|
|
+ // const modules = {
|
|
|
+ // name: 'imageResize',
|
|
|
+ // module: ImageResize,
|
|
|
+ // options: {
|
|
|
+ // modules: ['Resize'],
|
|
|
+ // },
|
|
|
+ // };
|
|
|
+
|
|
|
+ const editorRef = ref();
|
|
|
+ const content = ref<string>('');
|
|
|
+
|
|
|
+ const { parseRichText } = useParser();
|
|
|
+ const { renderRichText } = useRender();
|
|
|
+
|
|
|
+ function contentChange() {
|
|
|
+ if (!props.autoEmit) return;
|
|
|
+ const valJson = getValJson();
|
|
|
+ emit('update:modelValue', valJson);
|
|
|
+ emit('change', valJson);
|
|
|
+ }
|
|
|
+
|
|
|
+ function getValJson() {
|
|
|
+ const editorContainer = editorRef.value.getEditor();
|
|
|
+ return parseRichText(editorContainer.childNodes[0]);
|
|
|
+ }
|
|
|
+
|
|
|
+ watch(
|
|
|
+ () => props.modelValue,
|
|
|
+ (val) => {
|
|
|
+ let valJson = {} as RichTextJSON;
|
|
|
+ if (val === null || val === '') {
|
|
|
+ valJson = { sections: [] };
|
|
|
+ } else if (typeof val === 'string') {
|
|
|
+ try {
|
|
|
+ valJson = JSON.parse(val);
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ valJson = { sections: [] };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ valJson = val;
|
|
|
+ }
|
|
|
+ content.value = renderRichText(valJson);
|
|
|
+ },
|
|
|
+ {
|
|
|
+ immediate: true,
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ defineExpose({ getValJson });
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+ .rich-editor {
|
|
|
+ height: 400px;
|
|
|
+ }
|
|
|
+ .rich-editor em {
|
|
|
+ font-style: italic;
|
|
|
+ }
|
|
|
+</style>
|