github地址:https://github.com/501351981/vue_yii_cms
做一个后台,离不开文本编辑器,百度的ueditor经常用,这次改用vue写后台,需要再次集成一下,遇到很多问题,搞了一下午,整理一下,希望能够帮到后来者。
目标:
希望封装的ueditor组件,尽可能简单,期望是这样的,能够像input一样简单,可以实现v-mode
<ueditor v-model="content"></ueditor>
实现
复制ueditor文件
将下载的ueditor放到static/lib路径下面 ,这个位置随意,根据自己情况更改;后台相关的代码可以删除,交由后台同学处理,因为我们现在用vue是前后台分离的,所以不需要后台代码
更改配置文件
更改ueditor.config.js,
将 var URL = window.UEDITOR_HOME_URL || getUEBasePath(); 改为 var URL ="/static/lib/ueditor/" 也就是我们的ueditor路径,如果不这么改,你可能会看到如下报错
Uncaught SyntaxError: Unexpected token <
ZeroClipboard.js:1 Uncaught SyntaxError: Unexpected token <
ueditor.all.js?9bdc:14409 Uncaught ReferenceError: ZeroClipboard is not defined
at initZeroClipboard (ueditor.all.js?9bdc:14409)
at eval (ueditor.all.js?9bdc:14456)
at HTMLScriptElement.element.onload.element.onreadystatechange (ueditor.all.js?9bdc:921)
更改服务器接口地址,因为我们是前后台分离,所以之前的接口地址就不对了,要改成自己的真实接口地址
, serverUrl: process.env.API_ROOT + "/ueditor/php/controller.php"
没改正确报错
请求后台配置项http错误,上传功能将不能正常使用!
组件封装
新建一个Ueditor.vue文件,其中data中的editor为编辑器实例,为了实现v-mode功能,我们需要一个名为value属性,还需要在编辑器内容功能时,触发input事件,编辑器内容变化通过contentChange事件监听
另一个props属性为config,我们可以通过这个修改编辑器的样式等,此处给了一个默认值,高度350px
data中有两个属性
editor:保存编辑器对象实例
content:存储编辑器的内容,目的是监听value变化时,比对新value和content是否一样,一样就说明是因为编辑器内容更改,触发的更改value,此时不应该再根据这个值,重置编辑器内容,否则会造成编辑器光标错乱的尴尬情况
<template> <div class="ueditor"> <script id="editor" type="text/plain"></script> </div> </template> <script> import '../../../static/lib/ueditor/ueditor.config' import '../../../static/lib/ueditor/ueditor.all' import '../../../static/lib/ueditor/lang/zh-cn/zh-cn' import '../../../static/lib/ueditor/ueditor.parse' export default { name:'Ueditor', data:function () { return{ editor:'', content:'' } }, props: { config: { type: Object, default:function () { return { initialFrameWidth: null, initialFrameHeight: 350, lang:"zh-cn" } } }, value:String }, watch:{ value:function(new_content) { if(new_content!=this.content){ this.editor.setContent(this.value); } } }, mounted:function () { this.editor=UE.getEditor("editor",this.config) this.editor.addListener("ready", ()=>{ this.editor.setContent(this.value); // 确保UE加载完成后,放入内容。 this.editor.addListener("contentChange",()=>{ this.content=this.editor.getContent() this.$emit('input', this.content) }) }); }, methods: { getUEContent:function() { return this.editor.getContent() } }, destroyed: function() { this.editor.destroy(); }, } </script> <style lang="less"> </style>
注意上面的destroyed事件是必须的,如果没有这段代码,你会发现,页面刷新的时候能够看到编辑器,此后如果通过点击返回,或者通过router跳转,再回到编辑器页面,发现编辑器消失了
在编辑页面使用Ueditor组件
<template> <div> <form class="form-horizontal form-edit"> //其他代码 <ueditor v-model="content"></ueditor> //其他代码 </form> </div> </template> <script> import Ueditor from "../../components/mod/Ueditor" export default { name: 'NewsEdit', components: {Ueditor}, data:function () { return { content:'测试内容' }, } } </script>
如果不出意外,现在你的编辑器已经可以正常工作了
图片上传
编辑器离不了图片上传,而现在图片上传还是不行的,会发现有错误提示(后台上传接口要先搞好)
提示跨域问题,图片上传这里,百度用的form表单的提交,我们把它改成通过ajax提交就好了
DOMException: Blocked a frame with origin "http://127.0.0.1:8080" from accessing a cross-origin frame.
at HTMLIFrameElement.callback (webpack-internal:///./static/lib/ueditor/ueditor.all.js:24482:84)
此处改动较大,改动domUtils.on(input, 'change', function(){})中的内容
改动前
domUtils.on(input, 'change', function(){ if(!input.value) return; var loadingId = 'loading_' + (+new Date()).toString(36); var params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''; 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') || '') + '" >'); function callback(){ try{ var link, json, loader, body = (iframe.contentDocument || iframe.contentWindow.document).body, result = body.innerText || body.textContent || ''; json = (new Function("return " + result))(); link = me.options.imageUrlPrefix + json.url; if(json.state == 'SUCCESS' && json.url) { console.log("上传成功了已经") loader = me.document.getElementById(loadingId); loader.setAttribute('src', link); loader.setAttribute('_src', link); loader.setAttribute('title', json.title || ''); loader.setAttribute('alt', json.original || ''); loader.removeAttribute('id'); domUtils.removeClasses(loader, 'loadingclass'); } else { showErrorLoader && showErrorLoader(json.state); } }catch(er){ console.log("上传失败了") console.log(er) showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError')); } form.reset(); domUtils.un(iframe, 'load', callback); } 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 }); } } /* 判断后端配置是否没有加载成功 */ 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; } domUtils.on(iframe, 'load', callback); form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?':'&') + params); form.submit(); 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 }); } } });
改动后,使用的是原生的ajax上传
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("file", form[0].files[0] ); var xhr = null; //得到xhr对象 if(XMLHttpRequest){ xhr = new XMLHttpRequest(); }else{ xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.open("post", action, true);//设置提交方式,url,异步提交 xhr.onload = function () { var data = xhr.responseText; //得到返回值 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'); me.fireEvent('contentchange') } else { showErrorLoader && showErrorLoader(data.state); } form.reset(); } xhr.send(formData); 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 }); } } });
最初,我是想用axios进行ajax操作的,在ueditor.all.js开头处加入了
import axios from 'axios'
结果发现,再次进入编辑器输入内容时,会有报错,如下,试了很多办法没用解决,搞了很长时间,虽然不影响上传,但是受不了代码有错误提示,最终换成上面的纯js的ajax上传图片,这个问题可能是因为通过import引入文件,强制改为了js严格模式
ueditor.all.js?9bdc:28636 Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at HTMLDocument.countFn (ueditor.all.js?9bdc:28636)
幸好,问题一个一个都被解决了,学习就是这样,会遇到很多困难,多console.log,找原因,找解决办法,一个一个测试,最终战胜困难
祝好