vue进阶——整合富文本编辑器wangEditor
前言
个人整合wangEditor过程中的经验与总结
一、什么是wangEditor?
wangEditor是一款基于JavaScript的富文本编辑器插件,用于在网页中实现所见即所得的编辑功能。它提供了丰富的编辑功能,包括文字样式设置、插入图片、插入表格、代码高亮等。wangEditor易于集成和使用,支持自定义配置和扩展。
二、安装wangEditor
1. React
yarn add @wangeditor/editor-for-react
# 或者 npm install @wangeditor/editor-for-react --save
2. Vue2
yarn add @wangeditor/editor-for-vue
# 或者 npm install @wangeditor/editor-for-vue --save
3. Vue3
yarn add @wangeditor/editor-for-vue@next
# 或者 npm install @wangeditor/editor-for-vue@next --save
4. CDN
<!-- 引入 css -->
<link href="https://unpkg.com/@wangeditor/editor@latest/dist/css/style.css" rel="stylesheet">
<!-- 引入 js -->
<script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js"></script>
<script>
var E = window.wangEditor; // 全局变量
</script>
三、基本使用
1. vue2
<template>
<div style="border: 1px solid #ccc;">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editor"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden;"
v-model="html"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="onCreated"
/>
</div>
</template>
<script>
import Vue from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
export default Vue.extend({
components: { Editor, Toolbar },
data() {
return {
editor: null,
html: '<p>hello</p>',
toolbarConfig: { },
editorConfig: { placeholder: '请输入内容...' },
mode: 'default', // or 'simple'
}
},
methods: {
onCreated(editor) {
this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
},
},
mounted() {
// 模拟 ajax 请求,异步渲染编辑器
setTimeout(() => {
this.html = '<p>模拟 Ajax 异步设置内容 HTML</p>'
}, 1500)
},
beforeDestroy() {
const editor = this.editor
if (editor == null) return
editor.destroy() // 组件销毁时,及时销毁编辑器
}
})
</script>
<style src="@wangeditor/editor/dist/css/style.css"></style>
2. vue3
<template>
<div style="border: 1px solid #ccc">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden;"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
/>
</div>
</template>
<script>
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
export default {
components: { Editor, Toolbar },
setup() {
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
// 内容 HTML
const valueHtml = ref('<p>hello</p>')
// 模拟 ajax 异步获取内容
onMounted(() => {
setTimeout(() => {
valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
}, 1500)
})
const toolbarConfig = {}
const editorConfig = { placeholder: '请输入内容...' }
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
return {
editorRef,
valueHtml,
mode: 'default', // 或 'simple'
toolbarConfig,
editorConfig,
handleCreated
};
}
}
</script>
script setup
<template>
<div style="border: 1px solid #ccc">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden;"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
/>
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
import { onBeforeUnmount, ref, onMounted, shallowRef } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();
// 内容 HTML
const valueHtml = ref('<p>hello</p>');
// 模拟 ajax 异步获取内容
onMounted(() => {
setTimeout(() => {
valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>';
}, 1500);
});
const toolbarConfig = {};
const editorConfig = { placeholder: '请输入内容...' };
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor) {
editor.destroy();
}
});
const handleCreated = (editor) => {
editorRef.value = editor; // 记录 editor 实例,重要!
};
const mode = 'default'; // 或 'simple'
</script>
四、文件上传问题
此处以vue2+Springboot为例,代码仅供参考
1、前端重点在于editorConfig中fileName及server的配置
<template>
<div>
<div class="editor-container">
<Toolbar :editor="editor" :defaultConfig="toolbarConfig" :mode="mode" />
<el-button @click="save">保存</el-button>
<el-button @click="clear">清空</el-button>
<Editor v-model="html" :defaultConfig="editorConfig" :mode="mode" @onCreated="onCreated" />
</div>
</div>
</template>
<script>
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
export default {
components: {
Editor,
Toolbar
},
data() {
return {
editor: null,
html: '<p>文章内容</p>',
toolbarConfig: {},
editorConfig: {
placeholder: '请输入内容...',
MENU_CONF: {
uploadImage: {
fieldName: 'file',
server: 'http://localhost:8082/file/uploadTest'
}
}
},
mode: 'default'
};
},
methods: {
save() {
this.$store.state.articleContent = this.html;
},
clear() {
this.html = '';
},
onCreated(editor) {
this.editor = Object.seal(editor);
}
},
mounted() {
setTimeout(() => {
this.html = '<p>暂不支持视频,提交前记得保存</p>';
}, 2000);
},
beforeDestroy() {
const editor = this.editor;
if (editor == null) return;
editor.destroy();
}
};
</script>
<style src="@wangeditor/editor/dist/css/style.css"></style>
<style>
.editor-container {
border: 1px solid #ccc;
}
.editor-container>* {
border-bottom: 1px solid #ccc;
}
.editor-container>button {
border: 0;
}
.editor-container>.w-e-text-container {
height: 500px;
overflow-y: hidden;
}
</style>
2、后端重点在于返回类型及文件大小配置
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
@RestController
@CrossOrigin
@RequestMapping("/file")
public class FileController {
@PostMapping("/uploadTest")
public JSONObject uploadTest(@RequestParam(value = "file", required = false) MultipartFile file,HttpServletRequest request) throws Exception {
String url = upload(file,request);
JSONObject jsonObject = new JSONObject();
if(url != null){
jsonObject.put("errno",0);
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("url",url);
jsonObject1.put("alt","a2-w1p.jpeg");
jsonObject1.put("href",url);
jsonObject.put("data",jsonObject1);
}else {
jsonObject.put("errno",1);
jsonObject.put("message","上传图片失败");
}
return jsonObject;
}
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws IOException {
String filename = file.getOriginalFilename();
String path = request.getServletContext().getRealPath("/upload/");
saveFile(file, path);
return "http://localhost:8082/upload/" + filename;
}
public void saveFile(MultipartFile f, String path) throws IOException {
File upDir = new File(path);
if (!upDir.exists()) {
upDir.mkdir();
}
File file = new File(path + f.getOriginalFilename());
f.transferTo(file);
}
}
- yml文件大小配置
servlet:
multipart:
# 上传文件的最大大小限制
max-file-size: 10MB
# 请求的最大大小限制
max-request-size: 10MB