外观
(1)访问WangEditor官网可查看:https://www.wangeditor.com/v5/toolbar-config.html#getconfig
(2)本次一vue3为例
(3)安装
首先切换到view目录下

运行下方命令
bash
yarn add `@wangeditor`/editor
# 或者 npm install `@wangeditor`/editor --save
yarn add `@wangeditor`/editor-for-vue`@next`
# 或者 npm install `@wangeditor`/editor-for-vue`@next` --save(4)在前端view项目中创建一个名为wangEditor的文件夹,在此文件夹下创建一个名为WangEditor.vue的前段展示页面

(5)复制官网的代码到我们的前端页面
plain
<script setup>
// 引入 css
import '`@wangeditor`/editor/dist/css/style.css'
import {onBeforeUnmount, ref, shallowRef, onMounted, reactive} from 'vue'
import { Editor, Toolbar } from '`@wangeditor`/editor-for-vue'
import {ElMessage} from "element-plus";
import http from "@/utils/http";
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
//定义数据
const data = reactive({
article:{}
})
// 内容 HTML
const valueHtml = ref('')
// 模拟 ajax 异步获取内容
onMounted(() => {
setTimeout(() => {
valueHtml.value = ''
}, 1500)
})
const toolbarConfig = {}
const editorConfig = {
placeholder: '请输入内容...' ,
MENU_CONF: {}
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
//发布文章,向后端保存数据
const publish = () => {
//将编辑器的内容手动保存到对象中
data.article.content = valueHtml.value
//向后端发送请求保存数据
http.post('/article/publishArticle', data.article).then(res => {
if (res.code === 100200) {
ElMessage({
message: res.msg,
type: 'success',
})
// 发布成功,清空编辑器内容
editorRef.value.clear()
// 清空表单数据
data.article = {}
} else {
ElMessage({
message: res.msg,
type: 'error',
})
}
})
}
// 修改 uploadImage 菜单配置
editorConfig.MENU_CONF['uploadImage'] = {
server: http.defaults.baseURL + 'oss/uploadAliYunOSS',
// 后端接收的文件名称
fieldName: 'file',
maxFileSize: 2 * 1024 * 1024, // 1M
// 上传的图片类型
allowedFileTypes: ["image/*"],
// 小于该值就插入 base64 格式(而不上传),默认为 0
base64LimitSize: 10 * 1024, // 10MB
// 自定义插入返回格式【后端返回的格式】
customInsert(res, insertFn) {
if (res.code !== 100200) {
ElMessage.error("上传文件失败," + res.message)
return
}
insertFn(res.data, '', res.data)
},
// 携带的数据
meta: {
token: ''
},
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: true,
// 单个文件上传成功之后
onSuccess(file, res) {
if (res.code === 100200) {
console.log(res)
ElMessage.success(`${file.name} 上传成功`)
return
} else {
ElMessage.warning(`${file.name} 上传出了点异常`)
return
}
// console.log(`${file.name} 上传成功`, res)
//ElMessage.success(`${file.name} 上传成功`, res)
},
// 单个文件上传失败
onFailed(file, res) {
console.log(res)
ElMessage.error(`${file.name} 上传失败`)
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
console.log(err, res)
ElMessage.error(`${file.name} 上传出错`)
},
}
</script>
<template>
<el-form :model="data.article" label-width="auto" style="max-width: 600px">
<el-form-item label="文章标题">
<el-input v-model="data.article.title"/>
</el-form-item>
<el-form-item label="发表时间">
<el-date-picker
v-model="data.article.createdAt"
type="datetime"
placeholder="请选择发表时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 250px"
/>
</el-form-item>
</el-form>
<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>
<el-button type="primary" `@click`="publish">发表</el-button>
</template>
<style scoped>
</style>(6)因为需要储存所以我们创建一个数据库表(专门为了储存数据)

(7)使用Mtbatis-puls的数据库导出工具类导出并生成三层架构(具体生成过程不再展示)
plain
package com.xja.application.generation;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class CodeGeneration {
public static void main(String[] args) {
/**
* 先配置数据源
*/
MySqlQuery mySqlQuery = new MySqlQuery() {
`@Override`
public String[] fieldCustom() {
return new String[]{"Default"};
}
};
DataSourceConfig dsc = new DataSourceConfig.Builder("jdbc:mysql://192.168.25.3:3306/tools?&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false","root","MySQL5.7#")
.dbQuery(mySqlQuery).build();
//通过datasourceConfig创建AutoGenerator
AutoGenerator generator = new AutoGenerator(dsc);
Scanner scanner = new Scanner(System.in);
System.out.println("代码生成的绝对路径(右键项目->copy path):");
String projectPath = scanner.next();
System.out.println("请输入表名,多个英文逗号分隔,所有输入 all");
String s = scanner.next();
/**
* 全局配置
*/
//String projectPath = System.getProperty("user.dir"); //获取项目路径
String filePath = projectPath + "/src/main/java"; //java下的文件路径
GlobalConfig global = new GlobalConfig.Builder()
.outputDir(filePath)//生成的输出路径
.author("lizi")//生成的作者名字
//.enableSwagger() //开启swagger,需要添加swagger依赖并配置
.dateType(DateType.TIME_PACK)//时间策略
.commentDate("yyyy年MM月dd日")//格式化时间格式
.disableOpenDir()//禁止打开输出目录,默认false
.fileOverride()//覆盖生成文件
.build();
/**
* 包配置
*/
PackageConfig packages = new PackageConfig.Builder()
.entity("pojo")//实体类包名
.parent("com.xja.application")//父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名
.controller("web")//控制层包名
.mapper("mapper")//mapper层包名
.xml("mapper.xml")//数据访问层xml包名
.service("service")//service层包名
.serviceImpl("service.impl")//service实现类包名
//.other("output")//输出自定义文件时的包名
.pathInfo(Collections.singletonMap(OutputFile.xml, projectPath + "/src/main/resources/mapper")) //路径配置信息,就是配置各个文件模板的路径信息,这里以mapper.xml为例
.build();
/**
* 模板配置
*/
// 如果模板引擎是 freemarker
// String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
TemplateConfig template = new TemplateConfig.Builder()
// .disable()//禁用所有模板
//.disable(TemplateType.ENTITY)禁用指定模板
// .service(filePath + "/service.java")//service模板路径
// .serviceImpl(filePath + "/service/impl/serviceImpl.java")//实现类模板路径
// .mapper(filePath + "/mapper.java")//mapper模板路径
// .mapperXml("/templates/mapper.xml")//xml文件模板路路径
// .controller(filePath + "/controller")//controller层模板路径
.build();
/**
* 注入配置,自定义配置一个Map对象
*/
// Map<String,Object> map = new HashMap<>();
// map.put("name","young");
// map.put("age","22");
// map.put("sex","男");
// map.put("description","深情不及黎治跃");
//
// InjectionConfig injectionConfig = new InjectionConfig.Builder()
// .customMap(map)
// .build();
/**
* 策略配置开始
*/
StrategyConfig strategyConfig = new StrategyConfig.Builder()
.enableCapitalMode()//开启全局大写命名
//.likeTable()模糊表匹配
.addInclude(getTables(s))//添加表匹配,指定要生成的数据表名,不写默认选定数据库所有表
.addTablePrefix("tb_", "sys_","bus_","rel_","dic_") //设置忽略表前缀
//.disableSqlFilter()禁用sql过滤:默认(不使用该方法)true
//.enableSchema()启用schema:默认false
.entityBuilder() //实体策略配置
//.disableSerialVersionUID()禁用生成SerialVersionUID:默认true
.enableChainModel()//开启链式模型
.enableLombok()//开启lombok
.enableRemoveIsPrefix()//开启 Boolean 类型字段移除 is 前缀
.enableTableFieldAnnotation()//开启生成实体时生成字段注解
//.addTableFills()添加表字段填充
.naming(NamingStrategy.underline_to_camel)//数据表映射实体命名策略:默认下划线转驼峰underline_to_camel
.columnNaming(NamingStrategy.underline_to_camel)//表字段映射实体属性命名规则:默认null,不指定按照naming执行
.idType(IdType.AUTO)//添加全局主键类型
.formatFileName("%s")//格式化实体名称,%s取消首字母I
.build()
.mapperBuilder()//mapper文件策略
.enableMapperAnnotation()//开启mapper注解
.enableBaseResultMap()//启用xml文件中的BaseResultMap 生成
.enableBaseColumnList()//启用xml文件中的BaseColumnList
//.cache(缓存类.class)设置缓存实现类
.formatMapperFileName("%sMapper")//格式化Dao类名称
.formatXmlFileName("%sMapper")//格式化xml文件名称
.build()
.serviceBuilder()//service文件策略
.formatServiceFileName("%sService")//格式化 service 接口文件名称
.formatServiceImplFileName("%sServiceImpl")//格式化 service 接口文件名称
.build()
.controllerBuilder()//控制层策略
//.enableHyphenStyle()开启驼峰转连字符,默认:false
.enableRestStyle()//开启生成`@RestController`
.formatFileName("%sController")//格式化文件名称
.build();
/*至此,策略配置才算基本完成!*/
/**
* 将所有配置项整合到AutoGenerator中进行执行
*/
generator.global(global)
.template(template)
// .injection(injectionConfig)
.packageInfo(packages)
.strategy(strategyConfig)
.execute();
}
// 处理 all 情况
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}
}(8)编写后端ArticleController接待层中代码
plain
package com.xja.application.web;
import com.xja.application.pojo.Article;
import com.xja.application.service.ArticleService;
import com.xja.application.utils.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* <p>
* 前端控制器
* </p>
*
* `@author` lizi
* `@since` 2025年09月12日
*/
`@RestController`
`@RequestMapping`("/article")
public class ArticleController {
`@Autowired`
private ArticleService articleService;
`@PostMapping`("publishArticle")//上传
public R publishArticle(`@RequestBody` Article article) {
System.out.println("article = " + article);
return R.Success("上传成功",articleService.save(article));
}
}(9)上述代码中已包含调用后端接口

添加本地单个图片上传(上述代码已经编写完整,此处只是指出)

展示上述已经存储到后端的数据
(1)在文件夹showArticle下方创建一个名为ShowArticle.vue的1前端展示页面
plain
<script setup>
import http from "@/utils/http";
import {reactive} from "vue";
const data = reactive({
article:{},
})
const getArticle = () => {
http.get('/article/getArticle').then(res => {
console.log(res)
data.article = res.data;
})
}
getArticle();
</script>
<template>
<div v-html="data.article.content"></div>
</template>
<style scoped>
</style>(2)编写后端ArticleController接待层代码写固定的(上述代码已经包含调用后端接口方法)
plain
`@GetMapping`("getArticle")//获取展示
public R getArticle() {
return R.Success("获取Article成功",articleService.getById(2));
}(3)启动项目进行测试