Ueditor是一款由百度web前端研发部开发的所见即所得的开源富文本编辑器,具有轻量、可定制、用户体验优秀等特点。可以用于拓展ruoyi的富文本编辑工具
我相信很多人打开Ueditor官网时是这样的
虽然下边有教程,但是却不能嵌入ruoyi框架。若依官方有出教程http://doc.ruoyi.vip/ruoyi/document/cjjc.html#%E9%9B%86%E6%88%90ueditor%E5%AE%9E%E7%8E%B0%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A8%E5%A2%9E%E5%BC%BA
不相信我的可以去看官网,当然Ueditor插件可以去官网下载,因为官网的已经搭建好了。
链接:https://pan.baidu.com/s/1jJL4lK4909XC4C77kMN-Fg
提取码:bkej
下载后解压,将ueditor_config中的ueditor-config.json取出到ueditor文件夹外(取出后可以删掉ueditor_config文件夹),然后将ueditor文件夹放到若依项目的admin-src-main-resources-static-ajax-lib中,此文件夹中大多都是插件,例如summernote(若依默认集成的富文本插件)
最后将取出的ueditor-config.json放到resources文件下
在admin-main-java-resources-templates目录下找的include.html文件
在最后添加ueditor,引入插件
<div th:fragment="ueditor-js">
<script th:src="@{/ajax/libs/ueditor/ueditor.config.js}">script>
<script th:src="@{/ajax/libs/ueditor/ueditor.all.min.js}">script>
<script th:src="@{/ajax/libs/ueditor/lang/zh-cn/zh-cn.js}">script>
div>
以 公告 页面为例我们会修改两个页面,一个是新增页面(add),还有一个编辑页面(editor)
要更换Ueditor富文本工具,我们需要将所有的summernote替换掉。由于我们下载的插件包功能都已经齐全,所以不用怕替换已经优化过的summernote。
公告相关页面位置如下图
打开add页面后,将公告内容处更换为:
注:更改其他非通知页面注意更改标签的id和name或者class,下边的js中也需要随着更改
DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('新增通知公告')" />
head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-notice-add">
<div class="form-group">
<label class="col-sm-2 control-label is-required">公告标题:label>
<div class="col-sm-10">
<input id="noticeTitle" name="noticeTitle" class="form-control" type="text" required>
div>
div>
<div class="form-group">
<label class="col-sm-2 control-label">公告类型:label>
<div class="col-sm-10">
<select name="noticeType" class="form-control m-b" th:with="type=${@dict.getType('sys_notice_type')}">
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}">option>
select>
div>
div>
<div class="form-group">
<label class="col-sm-2 control-label">公告内容:label>
<div class="col-sm-10">
<script id="editor" name="noticeContent" type="text/plain" style="height: 300px;">script>
div>
div>
<div class="form-group">
<label class="col-sm-2 control-label">公告状态:label>
<div class="col-sm-10">
<div class="radio-box" th:each="dict : ${@dict.getType('sys_notice_status')}">
<input type="radio" th:id="${dict.dictCode}" name="status" th:value="${dict.dictValue}" th:checked="${dict.default}">
<label th:for="${dict.dictCode}" th:text="${dict.dictLabel}">label>
div>
div>
div>
form>
div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: ueditor-js" />
<script type="text/javascript">
var prefix = ctx + "system/notice";
//初始化编辑器
var ue = UE.getEditor('editor');
//此方法必须在外面,否则会导致下方submitHandler方法中的text定位不到
function getContentTxt() {
return UE.getEditor('editor').getContentTxt();
}
$("#form-notice-add").validate({
focusCleanup: true
});
//替换掉原有的submitHandler方法
function submitHandler() {
if ($.validate.form()) {
var text = getContentTxt();
if (text == '' || text.length == 0) {
$.modal.alertWarning("请输入公告内容!");
return;
}
$.operate.save(prefix + "/add", $('#form-notice-add').serialize());
}
}
script>
body>
html>
editor页面修改类似于add,但是也有不同
DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
<th:block th:include="include :: header('修改通知公告')" />
head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
<form class="form-horizontal m" id="form-notice-edit" th:object="${notice}">
<input id="noticeId" name="noticeId" th:field="*{noticeId}" type="hidden">
<div class="form-group">
<label class="col-sm-2 control-label is-required">公告标题:label>
<div class="col-sm-10">
<input id="noticeTitle" name="noticeTitle" th:field="*{noticeTitle}" class="form-control" type="text" required>
div>
div>
<div class="form-group">
<label class="col-sm-2 control-label">公告类型:label>
<div class="col-sm-10">
<select name="noticeType" class="form-control m-b" th:with="type=${@dict.getType('sys_notice_type')}">
<option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}" th:field="*{noticeType}">option>
select>
div>
div>
<div class="form-group">
<label class="col-sm-2 control-label">公告内容:label>
<div class="col-sm-10">
<script id="editor" name="noticeContent" type="text/plain" style="height: 300px;">script>
<textarea id="noticeContent" style="display: none;">[[*{noticeContent}]]textarea>
div>
div>
<div class="form-group">
<label class="col-sm-2 control-label">公告状态:label>
<div class="col-sm-10">
<div class="radio-box" th:each="dict : ${@dict.getType('sys_notice_status')}">
<input type="radio" th:id="${dict.dictCode}" name="status" th:value="${dict.dictValue}" th:field="*{status}">
<label th:for="${dict.dictCode}" th:text="${dict.dictLabel}">label>
div>
div>
div>
form>
div>
<th:block th:include="include :: footer" />
<th:block th:include="include :: ueditor-js" />
<script type="text/javascript">
var prefix = ctx + "system/notice";
//替换summernote相关的函数
$(function () {
var text = $("#noticeContent").text();
var ue = UE.getEditor('editor');
ue.ready(function () {
ue.setContent(text);
});
})
function getContentTxt() {
return UE.getEditor('editor').getContentTxt();
}
$("#form-notice-edit").validate({
focusCleanup: true
});
//这个同样要替换
function submitHandler() {
if ($.validate.form()) {
var text = getContentTxt();
if (text == '' || text.length == 0) {
$.modal.alertWarning("请输入通知内容!");
return;
}
$.operate.save(prefix + "/edit", $('#form-notice-edit').serialize());
}
}
script>
body>
html>
如果页面其他的方法跟summernote无关则不需要处理,如果有相关的话根据实际情况进行处理,一般功能尽量不要耦合,不然很难去更换summernote.
在admin-main-java-controller-common文件夹下新增一个UeditorController,内容如下:
package com.ruoyi.web.controller.common;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.config.ServerConfig;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.utils.file.FileUploadUtils;
/**
* Ueditor 请求处理
*
* @author ruoyi
*/
@SuppressWarnings("serial")
@Controller
@RequestMapping("/ajax/libs/ueditor")
public class UeditorController extends BaseController
{
private final String METHOD_HEAD = "ueditor";
private final String IMGE_PATH = "/ueditor/images/";
private final String VIDEO_PATH = "/ueditor/videos/";
private final String FILE_PATH = "/ueditor/files/";
@Autowired
private ServerConfig serverConfig;
/**
* ueditor
*/
@ResponseBody
@RequestMapping(value = "/ueditor/controller")
public Object ueditor(HttpServletRequest request, @RequestParam(value = "action", required = true) String action,
MultipartFile upfile) throws Exception
{
List<Object> param = new ArrayList<Object>()
{
{
add(action);
add(upfile);
}
};
Method method = this.getClass().getMethod(METHOD_HEAD + action, List.class, String.class);
return method.invoke(this.getClass().newInstance(), param, serverConfig.getUrl());
}
/**
* 读取配置文件
*/
public JSONObject ueditorconfig(List<Object> param, String fileSuffixUrl) throws Exception
{
ClassPathResource classPathResource = new ClassPathResource("ueditor-config.json");
String jsonString = new BufferedReader(new InputStreamReader(classPathResource.getInputStream())).lines().parallel().collect(Collectors.joining(System.lineSeparator()));
JSONObject json = JSON.parseObject(jsonString, JSONObject.class);
return json;
}
/**
* 上传图片
*/
public JSONObject ueditoruploadimage(List<Object> param, String fileSuffixUrl) throws Exception
{
JSONObject json = new JSONObject();
json.put("state", "SUCCESS");
json.put("url", ueditorcore(param, IMGE_PATH, false, fileSuffixUrl));
return json;
}
/**
* 上传视频
*/
public JSONObject ueditoruploadvideo(List<Object> param, String fileSuffixUrl) throws Exception
{
JSONObject json = new JSONObject();
json.put("state", "SUCCESS");
json.put("url", ueditorcore(param, VIDEO_PATH, false, fileSuffixUrl));
return json;
}
/**
* 上传附件
*/
public JSONObject ueditoruploadfile(List<Object> param, String fileSuffixUrl) throws Exception
{
JSONObject json = new JSONObject();
json.put("state", "SUCCESS");
json.put("url", ueditorcore(param, FILE_PATH, true, fileSuffixUrl));
return json;
}
public String ueditorcore(List<Object> param, String path, boolean isFileName, String fileSuffixUrl)
throws Exception
{
MultipartFile upfile = (MultipartFile) param.get(1);
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
String fileName = FileUploadUtils.upload(filePath, upfile);
String url = fileSuffixUrl + fileName;
return url;
}
}
添加后根据自己不同的项目包名将引入包删除后重新引入.
这里多说一句,RequestMapping指引的路径要跟ueditor.config.js中的一致,ueditor.config.js在我们放入的ajax-lib-ueditor中,当然其已经自动获取url了
如果有修改也需要修改此文件。
再提一句,我曾维护一个项目的upload被重写了,重写后的upload返回的值不是String类型
那么我们通过toString转为字符串是行不通的,会导致我们的图片没法回显,并且路径也不对,这时我们需要自己重写一个upload类,然后自定义一个根据文件路径上传
//根据文件路径上传,返回值为String
public static final String upload1(String baseDir, MultipartFile file) throws IOException
{
try
{
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
}
catch (Exception e)
{
throw new IOException(e.getMessage(), e);
}
}
//返回值为String
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException
{
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
{
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
File desc = getAbsoluteFile(baseDir, fileName);
file.transferTo(desc);
return getPathFileName(baseDir, fileName);
}
当然,这upload类所在的路径是(common模块)common-src-main-java-common-utils-file-FileUploadUtils.java文件中