后端DEMO:https://github.com/coderliguoqing/UeditorSpringboot

前端DEMO:https://github.com/coderliguoqing/ueditor-web

预览地址:https://coderliguoqing.github.io/ueditor-web/dist/#/ueditor

写在最前面的话:鉴于近期很多的博友讨论,说我按照文章的一步一步来,弄好之后,怎么会提示后端配置项http错误,文件上传会提示上传错误。这里提别申明一点,ueditor在前端配置好后,需要与后端部分配合进行,后端部分的项目代码git地址:https://github.com/coderliguoqing/UeditorSpringboot,然后将配置ueditor.config.js 里的serverUrl的前缀改陈你自己的后端访问的请求路径地址,文件上传的后端部分,只提供了demo,具体对接文件服务器的部分需要自己修改完成。

首先,谈下这篇文章中的前后端所涉及到的技术框架内容。

虽然是后端的管理项目,但整体项目,是采用前后端分离的方式完成,这样做的目的也是产品化的需求;

前端,vue+vuex+vue router+webpack+elementUI的方案完成框架的搭建,其中用到了superUI来作为后端登陆之后的主页面框架,中间集成vue的大型单页应用;

        后端,springboot+spring+springmvc+spring serurity+mybatis+maven+redis+dubbo

+zookeeper的方式来构建项目框架和管理,提供给前端restful风格的接口。此处还提供app端、PC WEB端的接口。

UEditor之前一直有在项目中使用,作为国内开源的富文本编辑器,有百度的强大技术支持,整体来说是不错的选择,百度也提供了php、asp、.net、jsp的版本。原有的项目是采用整体式的开发方式,采用的是jsp的页面开发技术,所以集成起来相对来说更加容易,只需要按照文档的方式将前端集成进去,然后后端拿到源码之后,针对文件上传的类修改最终存储的方法即可将文件等上传到本身的服务器了。

然而,由于决定了做前后端分离的方式,必然就会有新的坑,特别是还选择了新的技术vue.js+elementUI的这种方式。那么也只能放手一搏,不多啰嗦,介绍完,马上开始正事。

1、下载UEditor官网最新的jsp版本的包,下载完成解压之后得到一个ueditor1_4_3_3-utf8-jsp的文件夹,里面包含的内容如下:

除了jsp的文件夹之外,其余的文件和文件夹复制到前端项目中的static用于存放静态文件的目录下,结构如下:

这里特别说明jsp目录下的资源为何不放进来,因为我们是vue搭建的项目,jsp页面肯定是不会放在前端的项目中的,包括config.json也放在后端用于解析,这里后面会解释这样做的原因。

   2、前端将文件放进来之后,暂时先这样,咱们来整理后端的东西。这里将jsp目录下的lib目中的ueditor.jar文件中的所有类全部拿出来(具体方式自己决定,反编译工具或者拿到源码都可以),放到后端项目中,然后在control层新建一个UeditorController.java的类,如下:

 /**
* 用于处理关于ueditor插件相关的请求
* @author Guoqing
*
*/
@RestController
@CrossOrigin
@RequestMapping("/sys/ueditor")
public class UeditorController extends BaseController { @RequestMapping(value = "/exec")
@ResponseBody
public String exec(HttpServletRequest request) throws UnsupportedEncodingException{
request.setCharacterEncoding("utf-8");
String rootPath = request.getRealPath("/");
return new ActionEnter( request, rootPath).exec();
}
}

该类主要处理,ueditor与后端服务器的交互,通过action=''不同的类型来处理,其中action=config为加载配置项,action=uploadImg 图片上传,在ActionEntor类中,你可以根据不同的请求类型来处理;

处理当action=config时的情况,保证前端的编辑器各项文件上传功能,能够正常使用。

然后jsp目录下的config.json文件放到java/main/resources目录下,修改ConfigManager.java类,如下:

注释掉原有的读取配置文件的方式,添加新的读取路径,这样确保ueditor在初始化能够正确的加载配置文件。此时,修改前端项目中ueditor.config.js中的serverUrl的值为:

// 服务器统一请求接口路径
, serverUrl: "http://localhost:8080/sys/ueditor/exec"

而,针对ActionEnter.java类中,如下代码后的文件上传的处理,请大家针对自身的上传方式和文件服务器选择适合自己的方式:

switch ( actionCode ) {
//读取配置文件时的请求处理
case ActionMap.CONFIG:
return this.configManager.getAllConfig().toString();
//上传图片、视频、文件时的处理
case ActionMap.UPLOAD_IMAGE:
case ActionMap.UPLOAD_SCRAWL:
case ActionMap.UPLOAD_VIDEO:
case ActionMap.UPLOAD_FILE:
conf = this.configManager.getConfig( actionCode );
state = new Uploader( request, conf, baseFileService ).doExec();
break;
//抓取远程图片时的处理方式,此处也可以关闭 //当从网页上复制内容到编辑器中,如果图片与该域名不同源,则会自动抓到本地的服务器保存
case ActionMap.CATCH_IMAGE:
conf = configManager.getConfig( actionCode );
String[] list = this.request.getParameterValues( (String)conf.get( "fieldName" ) );
state = new ImageHunter( conf ).capture( list );
break;
//上传多文件时的文件在线管理
case ActionMap.LIST_IMAGE:
case ActionMap.LIST_FILE:
conf = configManager.getConfig( actionCode );
int start = this.getStartIndex();
state = new FileManager( conf ).listFile( start );
break; }
return state.toJSONString();

接下来是前端的处理,介于大家的要求。作者将原本的demo进行了优化,将编辑器封装成组件的方式,方便调用,代码如下:

<template>
<div>
<script id="editor" type="text/plain" ></script>
</div>
</template> <script>
import AppConfig from '@/config'
import '../../../../../../static/ueditor/ueditor.config.js'
import '../../../../../../static/ueditor/ueditor.all.js'
import '../../../../../../static/ueditor/lang/zh-cn/zh-cn.js' export default {
name: "UEditor",
props: {
id: {
type: String
},
config: {
type: Object
}
},
data() {
return {
editor: null
}
},
mounted() {
//初始化UE
const _this = this;
this.editor = UE.getEditor('editor',this.config);
},
destoryed() {
this.editor.destory();
},
methods:{
getUEContent: function(){
return this.editor.getContent();
}
}
}
</script>

导出组件:

var UEditor =  require('./src/ueditor.vue');

module.exports = {
UEditor
}

这里之所以是用一个模态框的方式来加载编辑器,是因为会存在编辑器工具栏浮动的问题,如果有问题,请根据如下的配置来处理即可;

页面调用:

<template>
<div id="app" class="hello">
<el-button size="primary" type="info" icon="plus" @click="openWindow">打开窗口</el-button>
<el-dialog title="新增菜单" size="small" v-model="addFormVisible" :close-on-click-modal="false">
<div>
<el-button size="primary" type="info" icon="plus" @click="getContent">获取内容</el-button>
<UEditor :config=config ref="ueditor"></UEditor>
</div>
</el-dialog> </div>
</template> <script>
import {UEditor} from './ueditor/index.js' export default{
name: 'hello',
components: {UEditor},
data(){
return {
config: {
/*//可以在此处定义工具栏的内容
toolbars: [
['fullscreen', 'source','|', 'undo', 'redo','|','bold', 'italic', 'underline', 'fontborder', 'strikethrough',
'|','superscript','subscript','|', 'forecolor', 'backcolor','|', 'removeformat','|', 'insertorderedlist', 'insertunorderedlist',
'|','selectall', 'cleardoc','fontfamily','fontsize','justifyleft','justifyright','justifycenter','justifyjustify','|',
'link','unlink']
],*/
autoHeightEnabled: false,
autoFloatEnabled: true,  //是否工具栏可浮动
initialContent:'请输入内容', //初始化编辑器的内容,也可以通过textarea/script给值,看官网例子
autoClearinitialContent:true, //是否自动清除编辑器初始内容,注意:如果focus属性设置为true,这个也为真,那么编辑器一上来就会触发导致初始化的内容看不到了
initialFrameWidth: null,
initialFrameHeight: 450,
BaseUrl: '',
UEDITOR_HOME_URL: 'static/ueditor/'
},
addFormVisible: false
}
},
methods: {
openWindow: function(){
this.addFormVisible = true;
},
//获取文档内容
getContent: function(){
let content = this.$refs.ueditor.getUEContent();
console.log(content);
alert(content);
}
}
} </script>

至此,大功告成,包括文件上传下载等部分全部搞定,不过要声明一点的是,当出现接口与页面部署域名不同时,点击选择图片上传会出现iframe跨域的问题。

2017-09-08的更新,作者已经通过更改源码的方式,处理了单图选择文件上传存在跨域的问题,处理方式当然就是将原有的form.submit的表单上传方式,更改为ajax的上传方式;

ueditor.all.js 24503行的方法替换,源码如下:

      /**
* 2017-09-07 改掉了ueditor源码,将本身的单文件上传的方法改为ajax上传,主要目的是为了解决跨域的问题
* @author Guoqing
*/
domUtils.on(input, 'change', function() {
if(!input.value) return;
var loadingId = 'loading_' + (+new Date()).toString(36);
var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'));
var allowFiles = me.getOpt('imageAllowFiles'); me.focus();
me.execCommand('inserthtml', '<img class="loadingclass" id="' + loadingId + '" src="' + me.options.themePath + me.options.theme +'/images/spacer.gif" title="' + (me.getLang('simpleupload.loading') || '') + '" >'); /!* 判断后端配置是否没有加载成功 *!/
if (!me.getOpt('imageActionName')) {
errorHandler(me.getLang('autoupload.errorLoadConfig'));
return;
}
// 判断文件格式是否错误
var filename = input.value,
fileext = filename ? filename.substr(filename.lastIndexOf('.')):'';
if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
showErrorLoader(me.getLang('simpleupload.exceedTypeError'));
return;
} var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '';
var action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?' : '&') + params);
var formData = new FormData();
formData.append("upfile", form[0].files[0] );
$.ajax({
url: action,
type: 'POST',
cache: false,
data: formData,
processData: false,
contentType: false,
success: function (data) {
data = JSON.parse(data);
var link, loader,
body = (iframe.contentDocument || iframe.contentWindow.document).body,
result = body.innerText || body.textContent || '';
link = me.options.imageUrlPrefix + data.url; if(data.state == 'SUCCESS' && data.url) {
loader = me.document.getElementById(loadingId);
loader.setAttribute('src', link);
loader.setAttribute('_src', link);
loader.setAttribute('title', data.title || '');
loader.setAttribute('alt', data.original || '');
loader.removeAttribute('id');
domUtils.removeClasses(loader, 'loadingclass');
} else {
showErrorLoader && showErrorLoader(data.state);
}
form.reset();
}
});
function showErrorLoader(title){
if(loadingId) {
var loader = me.document.getElementById(loadingId);
loader && domUtils.remove(loader);
me.fireEvent('showmessage', {
'id': loadingId,
'content': title,
'type': 'error',
'timeout': 4000
});
}
}
});

好了,如果以上对你有帮助的话,请顺手点个赞,谢谢各位大虾们啦!

效果图如下:

/************************华丽丽的分割线*************************/

2017-11-29 响应大家的要求,前后端项目都提供了源码的demo  请上github上查看,如还有问题,请多多交流

各位如果还有问题的,那么请入群吧: qq   581036486   入群密码:88888888

https://github.com/coderliguoqing

欢迎围观、点赞!

我只是一个不想做程序员的好程序员。

vue+Ueditor集成 [前后端分离项目][图片、文件上传][富文本编辑]的更多相关文章

  1. vue.js+UEditor集成 [前后端分离项目]

    首先,谈下这篇文章中的前后端所涉及到的技术框架内容. 虽然是后端的管理项目,但整体项目,是采用前后端分离的方式完成,这样做的目的也是产品化的需求: 前端,vue+vuex+vue router+web ...

  2. 前后端分离跨服务器文件上传-Java SpringMVC版

    近来工作上不上特别忙,加上对后台java了解一点,所以就抽时间,写了一个java版本的前后端分离的跨服务器文件上传功能,包括前后端代码. 一.Tomcat服务器部分 1.Tomcat服务器 单独复制一 ...

  3. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  4. ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

    一.前言 这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自 ...

  5. Vue项目图片剪切上传——vue-cropper的使用

    最近自己在研究vue,然后做了一个小型的后台管理系统用来练手,开发过程中,想到了剪切图片上传用户头像的需求.上网百度了一番,发现好多用的都是vue-cropper.我也就用了,个人感觉还是挺好用的.现 ...

  6. Win10环境前后端分离项目基于Vue.js+Django+Python3实现微信(wechat)扫码支付流程(2021年最新攻略)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_182 之前的一篇文章:mpvue1.0+python3.7+Django2.0.4实现微信小程序的支付功能,主要介绍了微信小程序内 ...

  7. 基于Vue的前后端分离项目实践

    一.为什么需要前后端分离 1.1什么是前后端分离  前后端分离这个词刚在毕业(15年)那会就听说过,但是直到17年前都没有接触过前后端分离的项目.怎么理解前后端分离?直观的感觉就是前后端分开去做,即功 ...

  8. Yii框架和Vue的完美结合完成前后端分离项目

    背景说明 本文假设你对Yii和Vue都比较熟悉,至少都在项目里用过,另外笔者新人,以后不定时放一些干货,欢迎程序媛关注 Yii是一个PHP全端框架,典型的mvc的项目结构,后端接口都是一个控制器里放了 ...

  9. 喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了

    折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...

随机推荐

  1. 在Mac OS X中部署Tomcat的经验

    因为前几天重装了Mac的系统.准备接下来把一些必需的实验环境都搭建起来.这里简单总结一下在Mac OS X上部署Tomcat应该注意的事情: 下载Tomcat的相应版本号,如http://tomcat ...

  2. Android requires compiler compliance level 5.0 or 6.0. Found &#39;1.4&#39; instead的解决的方法

    今天在eclipse里报这个错误:   Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead. Plea ...

  3. UVA1630 Folding 区间DP

    Folding Description   Bill is trying to compactly represent sequences of capital alphabetic characte ...

  4. 安装eclipse maven插件m2eclipse No repository found containing

    m2eclipse插件是Eclipse的一款Maven插件. 安装m2eclipse插件的步骤例如以下: 启动Eclipse,在菜单条中选择Help,然后选择Install New Software- ...

  5. Linux - Nginx配置反向代理。

    Nginx配置反向代理. 准备两台服务器 http://192.168.70.66 http://192.168.70.62 设置正则匹配(192.168.70.66) vim /usr/local/ ...

  6. [BZOJ 2100] Apple Delivery

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2100 [算法] Answer = min{ dist(PB,PA1) + dist( ...

  7. Node.js:Buffer

    ylbtech-Node.js:Buffer 1.返回顶部 1. Node.js Buffer(缓冲区) JavaScript 语言自身只有字符串数据类型,没有二进制数据类型. 但在处理像TCP流或文 ...

  8. 虚基类——(1)定义人员类Person: 公有成员:姓名(Name); 保护成员:性别(Gender),年龄(Age); 构造函数和析构函数

    题目描述: (1)定义人员类Person: 公有成员:姓名(Name): 保护成员:性别(Gender),年龄(Age): 构造函数和析构函数 (2) 从人员类Person派生学生记录类Student ...

  9. Blender插件之Panel

    目标 [x] 总结Blender之Panel 总结 Blender之Panel需要从Blender界面组成开始理解. 直观上Blender的界面层次为 Editors ‣ Regions ‣ (Tab ...

  10. Unity3d Vector3

    using UnityEngine; using System.Collections; public class test : MonoBehaviour { void Start () { Vec ...