赞
踩
vue+element ui的常规后管项目,现在需要用到集成了秀米编辑器的富文本编辑器,要求是在秀米编辑器上上传的图片在导出到项目页面富文本编辑器时,图片要存储到本地服务器而不是秀米的图片服务器。
秀米编辑器的要求的富文本编辑器内核得是ueditor的,不过用quill内核的也可以,quill的大家可以自行百度,我这里用的是ueditor内核来做的。官方是有集成秀米的示例的:ueditor集成秀米官方示例,但是只是ueditor集成,并不是在vue项目里面去做的,所以我们需要搞明白3个问题:
1.vue项目怎么使用ueditor?
2.ueditor怎么集成秀米?
3.vue后管项目里的ueditor怎么集成秀米?
我这里直接给出我选择的答案:
1.用vue-ueditor-wrap富文本组件
2.自定义ueditor工具栏按钮用来给秀米使用,点击后打开自定义html页面,页面里通过iframe打开秀米编辑器页面,自定义html页面通过iframe通信的方式来获取到秀米编辑器里的内容,最后返给ueditor编辑器
3.同样通过iframe来与秀米编辑器进行通信,在富文本组件初始化的时候注册自定义ueditor工具栏按钮,定义按钮点击事件,按钮按下时通过iframe打开秀米编辑器页面,通过与iframe通信的方式拿到秀米编辑器的内容并回写到富文本编辑器中。
1.先下载个ueditor,放置到vue项目的静态资源目录ueditor,我是在public下新建了个目录UE:
2.安装vue-ueditor-wrap,直接npm i vue-ueditor-wrap
即可,具体使用请参考官方文档:vue-ueditor-wrap
3.接入秀米编辑器,我把vue-ueditor-wrap自己封装了一个组件:
<template> <div> <vue-ueditor-wrap v-model="editorValue" :config="editorConfig" editor-id="xiumi-demo-01" :style="`width: ` + getEditorWidth"></vue-ueditor-wrap> <el-dialog title="秀米图文编辑器" :visible.sync="xiumiVisiable" width="500px" append-to-body> <iframe :style="dynamicStyle" id="xiumi" src="https://xiumi.us/studio/v5#/paper"></iframe> </el-dialog> </div> </template> <script> import VueUeditorWrap from 'vue-ueditor-wrap'; export default { name: 'UE', components: { VueUeditorWrap }, data() { return { xiumiVisiable: false, // 富文本编辑器配置 editorConfig: { // 访问 UEditor 静态资源的根路径,我放在public下的UE文件夹里,所以这里是/UE/ UEDITOR_HOME_URL: "/UE/", // 服务端接口,这个就是后端图片上传的接口 serverUrl: process.env.VUE_APP_BASE_API + "/sys/media", // 请求头 // headers: { Authorization: "Bearer " + getToken() }, // 图片前缀 // imageUrlPrefix: config.imgUrl, // serverUrl: "http://localhost/UE/php/controller.php", // 初始宽度 initialFrameWidth: '100%', // 初始高度 initialFrameHeight: 240, // 编辑器撑高 autoHeightEnabled: true, //是否开启远程图片抓取转存 catchRemoteImageEnable: true, //是否显示元素路径 elementPathEnabled: false, //是否开启字数统计 wordCount: true, //允许的最大字符数 maximumWords: 10000, // 工具栏项配置 toolbars: [[ 'bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', 'cleardoc', '|', 'rowspacingtop', 'rowspacingbottom', 'lineheight', '|', 'customstyle', 'paragraph', 'fontfamily', 'fontsize', '|', 'directionalityltr', 'directionalityrtl', 'indent', '|', 'justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|', 'link', 'unlink', 'anchor', '|', 'imagenone', 'imageleft', 'imageright', 'imagecenter', '|', 'simpleupload', 'insertimage', 'insertvideo', 'attachment', 'pagebreak', 'template', 'background', '|', 'horizontal', 'date', 'time', 'spechars', 'wordimage', '|', 'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow', 'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', 'splittocells', 'splittorows', 'splittocols', 'charts', '|', 'preview', 'searchreplace', '|' ]] }, messageHandleCount: 1, // editorValue : this.editorContent }; }, props: { width:{ type: String, default: "100%", }, editorContent: { type: String, default: "", }, }, computed: { getEditorWidth(){ return this.width }, editorValue: { get(){ return this.editorContent }, set(value){ this.$emit('saveEditor',value) } }, // 计算属性 dynamicStyle() { return { width: this.windowWidth + 'px', // 动态计算宽度 height: this.windowHeight + 'px', // 动态计算高度 }; }, windowWidth() { // 获取窗口宽度 return window.innerWidth * 0.85; }, windowHeight() { // 获取窗口高度 return window.innerHeight * 0.75; } }, mounted() { const that = this; UE.registerUI('dialog', function (editor, uiName) { var btn = new UE.ui.Button({ //这个name的值是固定的,不要修改 name: 'xiumi-connect', //自定义按钮的名称 title: '秀米', //自定义按钮的图标,用秀米的图标就行 cssRules: `background-image: url('../../../static/xiumi-connect-icon.png') !important; background-size: contain;`, //定义按钮按下时的事件 onclick: function () { that.xiumiVisiable = true; that.messageHandleCount = 1; that.$nextTick(() => { //以下是固定写法,不要修改 var xiumi = document.getElementById('xiumi'); var xiumi_url = "https://xiumi.us"; xiumi.onload = function () { // "XIUMI:3rdEditor:Connect" 是特定标识符,不能修改,大小写敏感 xiumi.contentWindow.postMessage('XIUMI:3rdEditor:Connect', xiumi_url); }; document.addEventListener("mousewheel", function (event) { event.preventDefault(); event.stopPropagation(); }); window.addEventListener('message', function (event) { if (event.origin == xiumi_url) { //以上是固定写法,不要修改 //这里就是秀米编辑器那边点击上面“导出”按钮后会触发的事件 //在这里就要去做值的接收了,我这里是把秀米编辑器返回的event.data作为emit参数返给父组件的富文本编辑器使用了 that.$emit('saveEditor',event.data) that.xiumiVisiable = false; that.$nextTick(()=>{ // v-if关闭了iframe弹窗,但是message监听还在,除非页面刷新,否则再次打开秀米弹窗导出后会重复调用接口 if(that.messageHandleCount > 0){ //这个是触发ueditor抓取远程图片到本地的 editor.fireEvent("catchRemoteImage"); that.messageHandleCount--; } }) } }, false); }) } }); return btn; }); }, methods: { }, }; </script>
父组件使用:
<UEditor :editorContent="currentTextData" @saveEditor="editorSave"></UEditor> <script> export default { name: 'form', data() { return { currentTextData:'', //富文本编辑器内容 } }, methods:{ //秀米编辑器的内容赋值给富文本编辑器 editorSave(data){ this.currentTextData= data }, } </script>
附上一张秀米的图标
完成以上步骤,你就可以正常使用一个集成了秀米编辑器的富文本编辑器了!
具体效果可以参考:秀米图文排版UEditor插件示例
但是这个时候你的富文本编辑器是无法进行图片的上传的,但你尝试上传图片时,会提示你后端上传配置有误,无法进行图片上传。
讲下怎么配置ueditor的后端图片上传接口(JAVA版)
1.JAVA这边要写个返回配置文件的GET请求的接口,接口仅返回一个json:
/* 前后端通信相关的配置,注释只允许使用多行方式 */ { /* 上传图片配置项 */ "imageActionName": "uploadimage", /* 执行上传图片的action名称 */ "imageFieldName": "upfile", /* 提交的图片表单名称 */ "imageMaxSize": 2048000, /* 上传大小限制,单位B */ "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */ "imageCompressEnable": true, /* 是否压缩图片,默认是true */ "imageCompressBorder": 1600, /* 图片压缩最长边限制 */ "imageInsertAlign": "none", /* 插入的图片浮动方式 */ "imageUrlPrefix": "", /* 图片访问路径前缀 */ "imagePathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ /* {filename} 会替换成原文件名,配置这项需要注意中文乱码问题 */ /* {rand:6} 会替换成随机数,后面的数字是随机数的位数 */ /* {time} 会替换成时间戳 */ /* {yyyy} 会替换成四位年份 */ /* {yy} 会替换成两位年份 */ /* {mm} 会替换成两位月份 */ /* {dd} 会替换成两位日期 */ /* {hh} 会替换成两位小时 */ /* {ii} 会替换成两位分钟 */ /* {ss} 会替换成两位秒 */ /* 非法字符 \ : * ? " < > | */ /* 具请体看线上文档: fex.baidu.com/ueditor/#use-format_upload_filename */ /* 涂鸦图片上传配置项 */ "scrawlActionName": "uploadscrawl", /* 执行上传涂鸦的action名称 */ "scrawlFieldName": "upfile", /* 提交的图片表单名称 */ "scrawlPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "scrawlMaxSize": 2048000, /* 上传大小限制,单位B */ "scrawlUrlPrefix": "", /* 图片访问路径前缀 */ "scrawlInsertAlign": "none", /* 截图工具上传 */ "snapscreenActionName": "uploadimage", /* 执行上传截图的action名称 */ "snapscreenPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "snapscreenUrlPrefix": "", /* 图片访问路径前缀 */ "snapscreenInsertAlign": "none", /* 插入的图片浮动方式 */ /* 抓取远程图片配置 */ "catcherLocalDomain": ["127.0.0.1", "localhost", "img.baidu.com"], "catcherActionName": "catchimage", /* 执行抓取远程图片的action名称 */ "catcherFieldName": "source", /* 提交的图片列表表单名称 */ "catcherPathFormat": "/ueditor/php/upload/image/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "catcherUrlPrefix": "", /* 图片访问路径前缀 */ "catcherMaxSize": 2048000, /* 上传大小限制,单位B */ "catcherAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 抓取图片格式显示 */ /* 上传视频配置 */ "videoActionName": "uploadvideo", /* 执行上传视频的action名称 */ "videoFieldName": "upfile", /* 提交的视频表单名称 */ "videoPathFormat": "/ueditor/php/upload/video/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "videoUrlPrefix": "", /* 视频访问路径前缀 */ "videoMaxSize": 102400000, /* 上传大小限制,单位B,默认100MB */ "videoAllowFiles": [ ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid"], /* 上传视频格式显示 */ /* 上传文件配置 */ "fileActionName": "uploadfile", /* controller里,执行上传视频的action名称 */ "fileFieldName": "upfile", /* 提交的文件表单名称 */ "filePathFormat": "/ueditor/php/upload/file/{yyyy}{mm}{dd}/{time}{rand:6}", /* 上传保存路径,可以自定义保存路径和文件名格式 */ "fileUrlPrefix": "", /* 文件访问路径前缀 */ "fileMaxSize": 51200000, /* 上传大小限制,单位B,默认50MB */ "fileAllowFiles": [ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" ], /* 上传文件格式显示 */ /* 列出指定目录下的图片 */ "imageManagerActionName": "listimage", /* 执行图片管理的action名称 */ "imageManagerListPath": "/ueditor/php/upload/image/", /* 指定要列出图片的目录 */ "imageManagerListSize": 20, /* 每次列出文件数量 */ "imageManagerUrlPrefix": "", /* 图片访问路径前缀 */ "imageManagerInsertAlign": "none", /* 插入的图片浮动方式 */ "imageManagerAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 列出的文件类型 */ /* 列出指定目录下的文件 */ "fileManagerActionName": "listfile", /* 执行文件管理的action名称 */ "fileManagerListPath": "/ueditor/php/upload/file/", /* 指定要列出文件的目录 */ "fileManagerUrlPrefix": "", /* 文件访问路径前缀 */ "fileManagerListSize": 20, /* 每次列出文件数量 */ "fileManagerAllowFiles": [ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb", ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid", ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml" ] /* 列出的文件类型 */ }
这里面我只用到了imageActionName、catcherActionName和catcherLocalDomain这三个,其他的直接用默认的就行,不用管,imageActionName、catcherActionName这2个参数是前端自定义的,记住了后面会用到,catcherLocalDomain是域名名单,ueditor不会对这个数组里的域名的图片进行远程抓取,正常默认就行了。
2.打开ueditor.all.js文件,搜索“getActionUrl”这个方法,这个方法做的就是通过actionName来判断此时接口请求的路径,我们JAVA后端自己写了接口,所以这个方法要调整下,下面附上我的:
getActionUrl: function(action){ var actionName = this.getOpt(action) || action, imageUrl = this.getOpt('imageUrl'), serverUrl = this.getOpt('serverUrl'); if(!serverUrl && imageUrl) { serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2'); } if(serverUrl) { // 初始化配置富文本编辑器 if(actionName == 'config'){ serverUrl = serverUrl + '/config' } // 富文本图片上传 else if(action == 'uploadimage' || action == 'uploadscrawl'){ serverUrl = serverUrl + '/imageUpload' } // 远程抓取图片 else if(action == 'imageUpload'){ serverUrl = serverUrl + '/catchRemoteFile' } // serverUrl = serverUrl + (serverUrl.indexOf('?') == -1 ? '?':'&') + 'action=' + (actionName || ''); return utils.formatUrl(serverUrl); } else { return ''; } }
可以看到我这里判断了actionName == ‘config’的时候,接口地址是serverUrl + ‘/config’,这里的serverUrl就是vue-ueditor-wrap配置里面的serverUrl:
serverUrl: process.env.VUE_APP_BASE_API + "/sys/media",
然后要对图片上传、涂鸦上传、远程抓取图片等actionName做判断,这里的actionName的值就是刚刚说到要记的JSON里面的imageActionName、catcherActionName了,imageActionName是图片上传的,catcherActionName是远程抓取图片的,ueditor.all.js这里根据json配置里面的值去写if判断就行。
3.后端的图片上传接口处理
public class UEditorController { @RequestMapping(value = "/imageUpload", method = RequestMethod.POST) @ResponseBody public Map<String, Object> imageUpload(HttpServletRequest request) { Map<String, Object> result = new HashMap<>(); try { // 从request中获取上传的图片数据 MultipartFile file = ((MultipartHttpServletRequest) request).getFile("upfile"); // TODO: 处理图片上传逻辑,例如存储到本地文件系统或数据库 // 返回图片的URL地址 result.put("state", "SUCCESS"); result.put("url", " result.put("title", file.getOriginalFilename()); result.put("original", file.getOriginalFilename()); } catch (Exception e) { result.put("state", "FAIL"); result.put("error", e.getMessage()); } return result; } }
图片的存储和处理根据项目实际情况去写就行,主要是这里的返回值一定要一样,返回4个参数:state是接收状态,成功SUCCESS,失败FAIL;url是图片的地址(用于富文本编辑器内展示);title是图片名称;original是原文件名。
4.打开ueditor.all.js文件,搜索“res.url”,会找到这个判断:
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { var res = JSON.parse(xhr.responseText) var link = config.imgURL + res.url; // me.options.imageUrlPrefix if (res.state == 'SUCCESS' && res.url) { var loader = me.document.getElementById(loadingId); loader.setAttribute('src', link); loader.setAttribute('_src', link); loader.setAttribute('title', res.title || ''); loader.setAttribute('alt', res.original || ''); loader.removeAttribute('id'); domUtils.removeClasses(loader, 'loadingclass'); me.fireEvent("contentchange"); } else { showErrorLoader(res.state); } } else { showErrorLoader(me.getLang('simpleupload.loadError')); }
这里的link就是图片上传完成后富文本编辑器上回显的图片url,所以这个url一定要是前端可以访问的,一般是网络地址,我的config.imgURL是图片存放地址的前缀,res.url是图片的名称,所以我这里做了字符串拼接,你们可以根据自己的实际情况修改。
特别说明:修改完ueditor.all.js,要对这个文件进行uglify压缩,压缩完改名为ueditor.all.min.js覆盖回去即可生效
我是用gulp来做的压缩
做到这里,你的富文本编辑器就可以正常的上传图片了!
这个时候你在富文本编辑器上上传的图片,图片的存放地址都会是你后端图片上传接口那边指定的地址:
但是当你从秀米编辑器那边上传图片做好排版导入回来到富文本编辑器后,你会发现图片地址都是在秀米的图片服务器上:
而秀米的图片服务器会锁图,用不得。
所以!
接下来要讲的是怎么对从秀米编辑器那边粘贴到ueditor富文本编辑器里的图片进行本地化
1.首先前端这边的vue-ueditor-wrap组件配置里面要打开远程抓取图片的配置,你们从上面源码里面也能看到:
//是否开启远程图片抓取转存
catchRemoteImageEnable: true,
2.后端写个抓取远程图片的接口,具体可以参考这篇文章ueditor,neditor配置远程图片抓取
3.打开ueditor.all.js文件,搜索“catchremoteimage”,直接把整个方法改成:
UE.plugins['catchremoteimage'] = function () { var me = this, ajax = UE.ajax; /* 设置默认值 */ if (me.options.catchRemoteImageEnable === false) return; me.setOpt({ catchRemoteImageEnable: false }); me.addListener("afterpaste", function () { me.fireEvent("catchRemoteImage"); }); me.addListener("catchRemoteImage", function () { var catcherLocalDomain = me.getOpt('catcherLocalDomain'), catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName')), catcherUrlPrefix = me.getOpt('catcherUrlPrefix'), catcherFieldName = me.getOpt('catcherFieldName'); var remoteImages = [], imgs = domUtils.getElementsByTagName(me.document, "img"), backgroundimagestags = domUtils.getElementsByTagName(me.document, "section span div p "),//抓取背景图片所在的标签 test = function (src, urls) { if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) { return true; } if (urls) { for (var j = 0, url; url = urls[j++];) { if (src.indexOf(url) !== -1) { return true; } } } return false; }; //img标签 for (var i = 0, ci; ci = imgs[i++];) { if (ci.getAttribute("word_img")) { continue; } var src = ci.getAttribute("_src") || ci.src || ""; if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) { remoteImages.push(src); } } //背景图片所在标签 var backgroundimages = []; //console.log("背景图片个数:" + backgroundimagestags.length); for (var i = 0, backci; backci = backgroundimagestags[i++];) { var bstyle = backci.style; var backgroundimgurltag = bstyle['background-image'] || bstyle['background'] || ""; if (backgroundimgurltag != null && backgroundimgurltag != "") { var backsrc = backgroundimgurltag.split("(")[1].split(")")[0].replace(/\"/g, "") || backgroundimgurltag.split("(")[1].split(")")[0].replace(/\"/g, "") || ""; //console.log("ci_src:" + backsrc); if (backsrc != null && backsrc != "") { if (/^(https?|ftp):/i.test(backsrc) && !test(backsrc, catcherLocalDomain)) { backgroundimages.push(backsrc); remoteImages.push(backsrc); } } } //console.log("remoteImages个数:" + remoteImages.length); } if (remoteImages.length) { me.fireEvent('catchremotestart'); catchremoteimage(remoteImages, { //成功抓取 success: function (r) { try { var info = r.state !== undefined ? r:eval("(" + r.responseText + ")"); } catch (e) { return; } /* 获取源路径和新路径 */ var i, j, ci, cj, oldSrc, newSrc, list = info.list; //img标签的替换 for (i = 0; ci = imgs[i++];) { oldSrc = ci.getAttribute("_src") || ci.src || ""; for (j = 0; cj = list[j++];) { if (oldSrc == cj.source && cj.state == "SUCCESS") { //抓取失败时不做替换处理 newSrc = cj.url; domUtils.setAttributes(ci, { "src": newSrc, "_src": newSrc }); break; } } } //背景图片地址的替换 var bodyHtml = me.document.body.innerHTML; //console.log("上传之前html:" + bodyHtml); for (var a = 0; a < backgroundimages.length; a++) { oldSrc = backgroundimages[a] || ""; for (j = 0; cj = list[j++];) { if (oldSrc == cj.source && cj.state == "SUCCESS") { //抓取失败时不做替换处理 newSrc = cj.url; // console.log("上传之后oldSrc:" + oldSrc); // console.log("上传之后newSrc:" + newSrc); // console.log("上传之后html:" + me.document.body.innerHTML.replace(oldSrc, newSrc)); bodyHtml = bodyHtml.replace(oldSrc, newSrc); break; } } } me.document.body.innerHTML = bodyHtml; me.fireEvent('catchremotesuccess'); me.fireEvent('catchremotecomplete'); }, //回调失败,本次请求超时 error: function () { me.fireEvent("catchremoteerror"); } }); } function catchremoteimage(imgs, callbacks) { imgs = imgs.toString(); var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '', url = utils.formatUrl(catcherActionUrl + (catcherActionUrl.indexOf('?') == -1 ? '?':'&') + params), isJsonp = utils.isCrossDomainUrl(url), opt = { 'method': 'POST', 'dataType': isJsonp ? 'jsonp':'json', 'timeout': 60000, //单位:毫秒,回调请求超时设置。目标用户如果网速不是很快的话此处建议设置一个较大的数值 'onsuccess': callbacks["success"], 'onerror': callbacks["error"] }; opt[catcherFieldName] = imgs; ajax.request(url, opt); } }); };
其实这里只是对秀米编辑器的部分标签做了识别,秀米编辑器过来的图片不完全都是img标签,所以会漏掉一些图片,这里加了一些对背景图片的处理。改完记得把js文件压缩改名覆盖回去。
4.最后一步就是在秀米编辑器那边导出数据到富文本编辑器的时候触发这个catchremoteimage方法即可完成秀米编辑器远程图片的抓取了,你们回到文章最上面看我最开始分享的组件源码就能看到触发的位置了:
//这个是触发ueditor抓取远程图片到本地的
editor.fireEvent("catchRemoteImage");
做完这些,一个可以在vue项目里面使用的集成了秀米编辑器且具备图片上传与秀米图片本地化的ueditor富文本编辑器就做好了,使用的时候贼拉方便,富文本编辑器内容直接通过变量绑定就完事,不再需要通过ueditor设值与取值方法去操作了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。