vue.js+UEditor集成 前後端分離 頂 原

後端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的類,如下:

1 /**
 2  * 用於處理關於ueditor插件相關的請求
 3  * @author Guoqing
 4  *
 5  */
 6 @RestController
 7 @CrossOrigin
 8 @RequestMapping("/sys/ueditor")
 9 public class UeditorController extends BaseController {
10 
11     @RequestMapping(value = "/exec")
12     @ResponseBody
13     public String exec(HttpServletRequest request) throws UnsupportedEncodingException{ 
14         request.setCharacterEncoding("utf-8");
15         String rootPath = request.getRealPath("/");
16         return new ActionEnter( request, rootPath).exec();
17     }
18 }

 該類主要處理,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

https://github.com/coderliguoqing

歡迎圍觀、點贊!

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章