富文本编辑器 WangEditor

1,649 阅读1分钟

wangeditor官网

  1. vue3中使用下载依赖
yarn add @wangeditor/editor 
yarn add @wangeditor/editor-for-vue@next
  1. 组件封装
// 组件文件
<template>
	<div style="border: 1px solid #ccc">
		<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
		<Editor style="height: 380px; overflow-y: hidden" v-model="currentText" :defaultConfig="editorConfig" :mode="mode" @onCreated="handleCreated" />
	</div>
</template>
<script setup>
import { onBeforeUnmount, watch, defineProps, shallowRef, reactive, defineEmits, toRefs } from 'vue';
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import { toolbarKeys } from './toolbar';
import Cookies from 'js-cookie';
import { ElMessage } from 'element-plus';
const emits = defineEmits(['update:modelValue']);
const state = reactive({
	mode: 'default',
	currentText: '',
});
const { mode, currentText } = toRefs(state);
const props = defineProps({
	valueHtml: String,
	isDisable: {
		type: Boolean,
		default: () => false,
	},
});
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();
// 菜单栏配置
const toolbarConfig = {
	toolbarKeys,
};
// 编辑器配置
const editorConfig = {
	placeholder: '请输入内容...',
	readOnly: props.isDisable,
	// 图片上传配置
	MENU_CONF: {
		uploadImage: {
			server: import.meta.env.VITE_API_URL + 'notice/upload',
			fieldName: 'file',
			// meta: {
			// 	token: 'xxx',
			// 	otherKey: 'yyy',
			// },
			// 自定义增加 http  header
			headers: {
				Authorization: `Bearer ${Cookies.get('token')}` || '',
			},
			maxFileSize: 10 * 1024 * 1024,
			// 最多可上传几个文件,默认为 100
			maxNumberOfFiles: 10,
			base64LimitSize: 10 * 1024, // 10kb
			// 超时时间,默认为 10 秒
			timeout: 5 * 1000, // 5 秒
			// 上传错误,或者触发 timeout 超时
			onBeforeUpload(file) {
				// 可以 return
				// 1. return file 或者 new 一个 file ,接下来将上传
				// 2. return false ,不上传这个 file
				return file;
			},
			// 上传错误,或者触发 timeout 超时
			onError(file, err) {
				const temp = 'Error: bg_img.svg exceeds maximum allowed size of 10 MB';
				const msg = err == temp ? '超出上限10M!' : msg;
				ElMessage({
					showClose: true,
					message: msg,
					type: 'warning',
				});
				return false;
			},
		},
	},
};

// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
	const editor = editorRef.value;
	if (editor == null) return;
	editor.destroy();
});
const handleCreated = (editor) => {
	editorRef.value = editor; // 记录 editor 实例,重要!
};
watch(
	() => props.valueHtml,
	(val) => {
		currentText.value = val;
	},
	{
		immediate: true,
	}
);
watch(
	() => currentText.value,
	(val) => {
		emits('update:modelValue', val);
	}
);
</script>
<style>
.w-e-text-placeholder {
	top: 10px;
}
</style>
// 菜单配置文件
export const toolbarKeys = [
	'headerSelect',
	'blockquote',
	// '|',
	'bold',
	'underline',
	'italic',
	{
		key: 'group-more-style',
		title: '更多',
		iconSvg:
			'<svg viewBox="0 0 1024 1024"><path d="M204.8 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M505.6 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path><path d="M806.4 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z"></path></svg>',
		menuKeys: ['through', 'code', 'sup', 'sub', 'clearStyle'],
	},
	'color',
	'bgColor',
	// '|',
	'fontSize',
	'fontFamily',
	'lineHeight',
	// '|',
	'bulletedList',
	'numberedList',
	'todo',
	{
		key: 'group-justify',
		title: '对齐',
		iconSvg:
			'<svg viewBox="0 0 1024 1024"><path d="M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',
		// menuKeys: ['justifyLeft', 'justifyRight', 'justifyCenter', 'justifyJustify'],
		menuKeys: ['justifyLeft', 'justifyRight', 'justifyCenter'],
	},
	{
		key: 'group-indent',
		title: '缩进',
		iconSvg:
			'<svg viewBox="0 0 1024 1024"><path d="M0 64h1024v128H0z m384 192h640v128H384z m0 192h640v128H384z m0 192h640v128H384zM0 832h1024v128H0z m0-128V320l256 192z"></path></svg>',
		menuKeys: ['indent', 'delIndent'],
	},
	// '|',
	'emotion',
	
	'insertLink',
	{
		key: 'group-image',
		title: '图片',
		iconSvg:
			'<svg viewBox="0 0 1024 1024"><path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z"></path></svg>',
		menuKeys: ['uploadImage','insertImage'],
	},
	'insertTable',
	'codeBlock',
	'divider',
	// '|',
	'undo',
	'redo',
	// '|',
	// 'fullScreen',
];

  1. 组件中使用
import Editor from '@/components/editor/index.vue';
<Editor :valueHtml="state.formData.content" v-model="state.formData.content" />