基于wangEditor 5实现一个简单的富文本编辑器组件,实现自定义上传图片。
官网地址:https://www.wangeditor.com/v5/for-frame.html#%E9%85%8D%E7%BD%AE
1. 安装依赖包:
npm i @wangeditor/editor --save
npm i @wangeditor/editor-for-vue --save
2. 封装组件代码:
<template><div class="editor-wrapper"><toolbar:editor="editor":default-config="toolbarConfig":mode="mode"/><editorv-model="html":style="{height: height + 'px'}":default-config="editorConfig":mode="mode"@onCreated="onCreated"@onChange="onChange"/><div v-if="maxlength" class="useful-num">{{ useLen }}/{{ maxlength }}</div></div>
</template><script>
import fileApi from '@/api/file';
import '@wangeditor/editor/dist/css/style.css';
import {Editor, Toolbar} from '@wangeditor/editor-for-vue';export default {name: 'customEditor',components: {Editor,Toolbar},model: {prop: 'value',event: 'change'},props: {value: {type: String,default: ''},maxlength: {type: Number,default: 0},height: {type: [String, Number],default: 300}},watch: {value(val) {this.html = val;}},data() {return {editor: null,html: '',toolbarConfig: { },editorConfig: {placeholder: '请输入内容',MENU_CONF: {uploadImage: {// 自定义图片上传功能customUpload: (resultFile, insertImgFn) => {const formData = new FormData();formData.append('file', resultFile);// 将文件上传至服务器,res.data返回服务器存放文件的urlfileApi.postFileUpload(formData).then(res => {// 插入图片,三个参数分别对应,url alt hrefinsertImgFn(res.data, '', res.data);});}},uploadVideo: {// 自定义视频上传功能customUpload: (resultFile, insertImgFn) => {const formData = new FormData();formData.append('file', resultFile);// 将文件上传至服务器,res.data返回服务器存放文件的urlfileApi.postFileUpload(formData).then(res => {// 插入视频,三个参数分别对应,url alt hrefinsertImgFn(res.data, '', res.data);});}}}},mode: 'default', // or 'simple'useLen: 0};},methods: {onCreated(editor) {this.editor = Object.seal(editor); // 一定要用 Object.seal() ,否则会报错},onChange() {const text = this.editor.getText();// 计算当前输入了多少文字this.useLen = (text || '').length;// 每次富文本内容改变,触发change事件this.$emit('change', this.html);}},beforeDestroy() {// editor销毁const editor = this.editor;if (editor == null) {return;}editor.destroy();}
};
</script><style lang="scss" scoped>.editor-wrapper{z-index: 3;position: relative; /deep/.w-e-toolbar{z-index: 2!important;border: solid 1px #E6E9EC!important;border-top-left-radius: 6px;border-top-right-radius: 6px;.w-e-bar-item{padding: 1px;}}/deep/.w-e-text-container{z-index: 1!important;border: solid 1px #E6E9EC!important;border-top: none!important;border-bottom-left-radius: 6px;border-bottom-right-radius: 6px;}}.useful-num{position: absolute;right: 6px;bottom: 10px;z-index: 99999;font-size: 12px;color: $text-3;background: #fff;padding: 0 6px;height: 28px;line-height: 28px;}
</style>
3. 父组件使用:
<template><editorv-model="html":maxlength="8192"@change="change"/>
</template><script>
import Editor from '@/components/Editor';
export default {name: 'EditorExample',components: {Editor},data() {return {html: ''};},methods: {change(val) {console.log(val);}}
};
</script>
4. 真实项目使用:
封装了wangEditor富文本组件,并且设置了数据回显;
TextRich/index.vue
<template><div style="border: 1px solid #ccc; width: 100%"><Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :defaultConfig="toolbarConfig" :mode="mode" /><Editorstyle="height: 500px; overflow-y: hidden"v-model="html":defaultConfig="editorConfig":mode="mode"@onCreated="handleCreated"@onChange="handleChange"/></div>
</template>
<script>
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { DomEditor } from '@wangeditor/editor'
import { set } from 'vue'
export default {components: {Editor,Toolbar},props: {value: {type: String,default: ''}},watch: {value(val) {setTimeout(()=>{this.html = val}, 1000)}},data() {return {editor: null,html: '',mode: 'default',editorConfig: {// placeholder: '请输入内容...',backColor: 'red', // 背景颜色MENU_CONF: {// 配置上传图片uploadImage: {customUpload: this.uploaadImg},uploadVideo: {customUpload: this.uploaadVideo}}},toolbarConfig: {}}},mounted() {// // 模拟 ajax 请求,异步渲染编辑器// setTimeout(() => {// this.html =// '<p>tupian </p><p>tupian</p><p><br></p><p><img src="https://img1.baidu.com/it/u=608404415,1639465523&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800" alt="tupian" data-href="https://img1.baidu.com/it/u=608404415,1639465523&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800" style=""/></p>'// }, 1500)},methods: {handleCreated(editor) {this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错// 设置工具栏详情this.toolbarConfig = {excludeKeys: ['insertVideo', 'uploadVideo', 'group-video', 'fullScreen']}},handleChange(content) {const toolbar = DomEditor.getToolbar(content)// 查看工具栏列表toolbar.getConfig().toolbarKeysthis.$emit('change', this.html)},uploaadImg(file, insertFn) {this.$emit('uploadImg', file, insertFn)},uploaadVideo(file, insertFn) {this.$emit('uploadVideo', file, insertFn)}},beforeDestroy() {const editor = this.editorif (editor == null) returneditor.destroy() // 销毁编辑器}
}
</script>
<style src="@wangeditor/editor/dist/css/style.css"></style>
父组件使用:
父组件是在el-form表中使用的
<template><div class="project-container"><el-form ref="form" :model="ruleForm" label-position="left" class="form"><el-form-item label="复现步骤" prop="reproduceStep" label-width="100px"><RichText v-model="ruleForm.reproduceStep" :readOnlys="readOnlys" @change="richTextChangeData" @uploadImg="richTextUploadImg"></RichText></el-form-item></el-form></div>
</template><script lang="ts">
import Vue from 'vue'
import RichText from '@/components/RichText/index.vue'export default Vue.extend({name: 'Index',components: {RichText},data() {return {ruleForm: {reproduceStep: ''}}},mounted() {this.setFormData()},methods: {richTextChangeData(val: string) {// 获取最新的html数据// this.ruleForm.reproduceStep = val},richTextUploadImg(file: any, insertFn: any) {// 插入图片,调接口返回图片url,通过插入contenglet imgUrl = 'https://img1.baidu.com/it/u=608404415,1639465523&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800'insertFn(imgUrl)// 设置只读this.readOnlys = true},setFormData() {this.ruleForm.reproduceStep ='<p>tupian </p><p>tupian</p><p><br></p><p><img src="https://img1.baidu.com/it/u=608404415,1639465523&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800" alt="tupian" data-href="https://img1.baidu.com/it/u=608404415,1639465523&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800" style=""/></p>'}}
})
</script>