SpringBoot3+Vue3实现富文本编辑器

先做一个模块

旅游攻略: 封面图,标题,攻略内容,发布时间

1
2
3
4
5
6
7
8
CREATE TABLE `introduction` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
`img` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '封面图',
`content` longtext COLLATE utf8mb4_unicode_ci COMMENT '攻略内容',
`title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '攻略标题',
`time` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '发布时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='旅游攻略表';

1.wangeditor 官网

wangEditor

2.安装

1
2
3
// cd vue
npm install @wangeditor/editor --save
npm install @wangeditor/editor-for-vue@next --save

3.引入

1
2
3
import '@wangeditor/editor/dist/css/style.css' //引入css
import {onBeforeUnmount,reactive,ref,shallowRef} from "vue";
import { Editor,Toolbar } from '@wangeditor/editor-for-vue'

4.初始化(表单中)

wangEditor 5富文本字段可以直接和form中的字段使用v-model进行绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<el-form-item prop="content" label="攻略详情">
<div style="border: 1px solid #ccc; width: 100%">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden;"
v-model="data.form.content"
:mode="mode"
:defaultConfig="editorConfig"
@onCreated="handleCreated"
/>
</div>
</el-form-item>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const toolbarConfig = {} //我加了这个 才能把图片放进去 :defaultConfig="editorConfig" :defaultConfig="toolbarConfig"
/* wangEditors 初始化开始 */
const editorRef = shallowRef() // 编辑器实例,必须用 shallowRef
const mode = 'default'
const editorConfig = { MENU_CONF: {} }
// 图片上传配置
editorConfig.MENU_CONF['uploadImage'] = {
headers: {
token: data.user.token,
},
server: "http://localhost:9999/files/wang/upload", // 服务器端图片上传接口
fieldName: 'file' // 服务端图片上传接口参数
}
// 组件销毁时,也及时销毁编辑器,否则可能会造成内存泄漏
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor === null) return
editor.destroy()
})
// 记录 editor 实例,重要!
const handleCreated = (editor) => {
editorRef.value = editor
}
/* wangEditor5 初始化结束 */

image-20250306084826077.png

Manager.vue

1
<el-menu-item index="/manager/introduction">旅游攻略</el-menu-item> # 新增一个菜单栏

新建Introduction.vue

Elmentplus中上传的图片是不是有大小限制?怎么修改

在 Element Plus 的 el-upload 组件中,默认没有内置的文件大小限制 ,但通常需要结合以下两种方式来限制上传文件的大小:

前端限制(推荐)

通过 before-upload 钩子函数在前端进行文件大小校验:

1
2
3
4
5
6
7
8
9
<el-upload
action="http://localhost:9999/files/upload"
:headers="{ token: data.user.token }"
:on-success="handleFileSuccess"
list-type="picture"
:before-upload="beforeFileUpload" <!-- 添加 before-upload 钩子 -->
>
<el-button type="primary">上传头像</el-button>
</el-upload>
1
2
3
4
5
6
7
8
9
10
11
// 在 methods 中定义校验逻辑
methods: {
beforeFileUpload(file) {
const maxSize = 2 * 1024 * 1024; // 限制为 2MB
if (file.size > maxSize) {
this.$message.error('文件大小不能超过 2MB!');
return false; // 阻止上传
}
return true; // 允许上传
}
}

后端限制(必须配置)

前端校验可以被绕过,因此必须在后端也进行文件大小限制 。以下是常见后端框架的配置示例:

1
2
3
4
5
6
# application.yml
spring:
servlet:
multipart:
max-file-size: 2MB # 单文件大小限制
max-request-size: 2MB # 请求总大小限制

其他注意事项

  1. 文件类型限制 :通过 accept 属性限制上传格式:

    1
    2
    3
    4
    <el-upload
    accept=".jpg,.jpeg,.png"
    ...
    />

    多文件上传 :如果需要单文件上传,设置 limit="1"

    1
    2
    3
    4
    <el-upload
    :limit="1"
    ...
    />

    错误处理 :通过 on-error 钩子捕获上传失败:

    1
    2
    3
    4
    <el-upload
    :on-error="handleFileError"
    ...
    />
    • 前端校验 :提升用户体验,即时反馈。
    • 后端校验 :确保安全性,防止恶意绕过。
    • 配置需一致 :前后端的文件大小限制应保持一致。

image-20250306083051771.png

直接复用之前做过的

image-20250306085425306.png

解决方法:点击按钮来查看

1
2
3
4
5
6
7
8
9
<el-table-column prop="content" label="攻略内容">
<template v-slot="scope">
<el-button type="primary" @click="viewContent(scope.row.content)">查看内容</el-button>
</template>
</el-table-column>

<el-dialog title="攻略详情" v-model="data.viewVisible" width="50%" destroy-on-close>
<div style="padding: 0 28px" v-html="data.viewContent"></div>
</el-dialog>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const data = reactive({
user: JSON.parse(localStorage.getItem('code2025_user') || "{}"),
title: null,
pageNum: 1,
pageSize: 5,
total: 0,
tableData: [],
form: {},
formVisible: false,
content: null,
viewVisible: false,


const viewContent = (content) => {
data.content = content
data.viewVisible = true
}

后端文件上传接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* wang-editor编辑器文件上传接口
*/
@PostMapping("/wang/upload")
public Map<String, Object> wangEditorUpload(MultipartFile file) {
String flag = System.currentTimeMillis() + "";
String fileName = file.getOriginalFilename();

try {
String filePath = System.getProperty("user.dir")+ "/files/";
// 文件存储形式:时间戳-文件名
FileUtil.writeBytes(file.getBytes(), filePath + flag + "-" + fileName);
System.out.println(fileName + "--上传成功");
Thread.sleep(1L);
} catch (Exception e) {
System.err.println(fileName + "--文件上传失败");
}

String http ="http://localhost:9999/files/download/";
Map<String, Object> resMap = new HashMap<>();
// wangEditor上传图片成功后,需要返回的参数
resMap.put("errno", 0);
resMap.put("data", CollUtil.newArrayList(Dict.create().set("url", http + flag + "-" + fileName)));
return resMap;
}