最近在写项目时,需要使用编辑器来编辑一个公司的描述信息,类似于这种效果:

在潘老师的指点下,一开始使用的TinyMCE这款编辑器,这也是一款很优秀的编辑器框架,但是后来提出了新的需求,在显示公司详情时,我们还需要能够插入公司的位置信息,类似于这样:

要实现这效果,我们需要调用百度地图的api,潘老师还推荐了百度的一款编辑器:ueditor,既然如此,既然如此,那就使用这款ueditor编辑器好了。

下载查看原型

我也不知道百度怎么想的,官网的演示既上传不了图片,也不能搜索地图,既然如此,只能把它下下来看看效果了。

之前写过php,就选择了php版本下载,打开解压,文件夹目录如下:

dialogs: 弹出对话框对应的资源和JS文件
lang: 编辑器国际化显示的文件
php或jsp或asp或net: 涉及到服务器端操作的后台文件
themes: 样式图片和样式文件
third-party: 第三方插件(包括代码高亮,源码编辑等组件)
ueditor.all.js: 开发版代码合并的结果,目录下所有文件的打包文件
ueditor.all.min.js: ueditor.all.js文件的压缩版,建议在正式部署时采用
ueditor.config.js: 编辑器的配置文件,建议和编辑器实例化页面置于同一目录
ueditor.parse.js: 编辑的内容显示页面引用,会自动加载表格、列表、代码高亮等样式,具体看内容展示文档
ueditor.parse.min.js: ueditor.parse.js文件的压缩版,建议在内容展示页正式部署时采用

这里php就是它的后端了,我们有自己的后端,先不理会它,在这个目录下使用http-serve,打开浏览器http://127.0.0.1:8080/就能看到原型了:

点开地图,选择动态地图后点击确认,但是却没有效果,原因是因为动态地图的实现原理是用<iframe>标签加载上述dialogs文件夹下的/map/show.html,将经度和纬度通过url参数传给show.html,它再调用百度地图api将位置信息显示出来,但是ueditor编辑器为了防止xss攻击,把<iframe>标签过滤掉了。 如果没有过滤掉,编辑器内容应该如下。

好在有解决方法,还记得在我们解压的文件夹下有一个ueditor.config.js文件,这就是编辑器的配置文件,打开它在里边查找whitList这一项,这就是xss过滤的白名单,添加iframe['frameborder','border','marginwidth','marginheight','width','height','src','id'],
这一项,我们的动态地图就不会被过滤掉了。


导入angular

因为我们项目使用的是angular,所以需要把ueditor导入到我们的项目来,将整个文件夹复制到assets下,并改名为ueditor

为了方便,我们使用 npm 下载 ngx-ueditor(可以看一下文档,挺少的)

在AppModule导入UEditorModule并配置:

imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    LandingPageModule,
    UEditorModule.forRoot({
      js: [
        `./assets/ueditor/ueditor.config.js`,
        `./assets/ueditor/ueditor.all.js`,
      ],
      // 默认前端配置项
      options: {
        UEDITOR_HOME_URL: './assets/ueditor/'
      }
    })
  ],

然后我们在app.component.html使用

<ueditor [(ngModel)]="html"></ueditor>

如果你看到了编辑器,那就说明成功的把ueditor导入到angular了。

配置后端

ueditor的后台代码都有它自己的规范,与我们的后台代码规范不一样,既然如此,只能修改它的开发代码以适应我们的后端了。
后端的配置,主要配置图片,视频的上传和获取,我们只要保证我们后台的接口接收和返回的数据格式与它要求的一致就行了。
打开控制台:

报了一个404的错误,请求的路径为:
http://localhost:8030/php/controller.php?action=config&&noCache=1567606213473 ,这个请求是用来请求后端的配置的,ueditor编辑器在初始化时,就需要后端的配置来配置上传的信息。

点开/php/config.json文件,这就是我们要返回的后端配置。

为了方便,我们直接返回这个配置信息。观察上述控制台,报错的行数是在ueditor.all.js:8111:

查看源代码发现它根据一个路径(后边会修改这个)请求后台的接口,并用返回来的json数据进行配置。所以在这里,直接把json数据返回就行了。
assets/ueditor/php/config.json移动到assets/ueditor/下,修改上述代码configUrl为:

var configUrl = 'assets/ueditor/config.json',

这时候我们就能成功使用图片上传的插件了。

接下来,我们还要配置后端上传的接口,首先修改根路径,在ueditor.config.js里找到serverUrl这一项,可以看到它的默认值是:serverUrl: URL + "php/controller.php",有关上传的所有功能,都会通过
http://localhost:8030/php/controller.php?action=xxxx
等方式进行上传,而action的名字就在后端配置config.json中,例如在config.json里的上传图片这一项:

{
    /* 上传图片配置项 */
    "imageActionName": "uploadimage", /* 执行上传图片的action名称 */
    "imageFieldName": "upfile", /* 提交的图片表单名称 */
    "imageMaxSize": 2048000, /* 上传大小限制,单位B */
    "imageAllowFiles": [".png", ".jpg", ".jpeg", ".gif", ".bmp"], /* 上传图片格式显示 */
    "imageCompressEnable": true, /* 是否压缩图片,默认是true */
    "imageCompressBorder": 1600, /* 图片压缩最长边限制 */
    "imageInsertAlign": "none", /* 插入的图片浮动方式 */
    "imageUrlPrefix": "", /* 图片访问路径前缀 */
.....
}

ueditor编辑器在一开始就要获取config.json文件的原因就是为了配置后端的上传接口,它会根据你在这里定义的配置来进行文件的上传.

首先,我们先修改serverUrl的值,改为你后台的地址,其次它的请求方法名是传递action=XXX参数,再由你后台获取这个参数进行判断,然后执行你需要的操作的,但是我们的后台接口一般都是在url上以/action来区分你要请求的方法,所以,还得改一下它的请求方法。
搜索getActionUrl,可以看到请求的接口地址都是从这里返回的,因此,我们只需要修改返回的格式就行了。

改为:

serverUrl = serverUrl + '/' + (actionName || '');

此时编写后台接口,我用的是springboot:

@RestController
@RequestMapping("ueditor")
@Api(tags = "Ueditor 临时上传文件接口")
public class UeditorController {

    @Autowired
    AttachmentService attachmentService;

    @ApiOperation(value = "上传图片类型的附件", notes = "上传图片类型的附件", nickname = "Attachment_uploadImage")
    @PostMapping("/uploadImage")
    @ResponseStatus(HttpStatus.CREATED)
    public UeditorImage uploadImage(@RequestParam("file") MultipartFile[] submissions) {
        MultipartFile submission = submissions[0];
        Attachment attachment = this.attachmentService.uploadImage(submission);
        UeditorImage ueditorImage = new UeditorImage();
        ueditorImage.setState("SUCCESS");
        ueditorImage.setTitle(attachment.getSaveName());
        ueditorImage.setOriginal(submission.getOriginalFilename());
        ueditorImage.setUrl(attachment.getSavePath());
        return ueditorImage;
    }
}

// 需要返回此json数据 给ueditor显示
class UeditorImage {
    private String state;
    private String url;
    private String title;
    private String original;

    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getOriginal() {
        return original;
    }
    public void setOriginal(String original) {
        this.original = original;
    }
}

注意:
PostMapping必须要与config.json 里的imageActionName一致。
@RequestParam("file")参数名必须要与imageFieldName一致。
返回json数据要与百度要求的json数据一致,不然前端无法显示。

好了,此时就能成功的上传图片了。此时我们绑定的([ngmodel])=html,html中存放的就是编辑框内容的html代码了,我们只要保存这个值到数据库,要显示详情的时候用<div [innerhtml]="xxxx"></div>就可以成功显示详情了。

注意,angular的innerhtml也会过滤掉iframe,因此我们要使用angular内置方法来转换我们的描述文本,告诉angular这是安全的。

import {Component} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';

@Component({
  selector: 'yzpf-root',
  template: '<div class="row col-md-6 offset-3" [innerHTML]="transform(html)">\n' +
    '</div>\n',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  html = '<p><img src="attachment/image/2019731/bdf4e0a70bd90c650fd32ebdc675c24c.png" title="" alt="6a600c338744ebf803157a9adef9d72a6059a72a.png" width="534" height="306"/></p><p>哔哩哔哩(英文名称:bilibili,简称B站)现为国内领先的年轻人文化社区,该网站于2009年6月26日创建,被粉丝们亲切的称为“B站”。</p><p>B站的特色是悬浮于视频上方的实时评论功能,爱好者称其为“弹幕”,这种独特的视频体验让基于互联网的弹幕能够超越时空限制,构建出一种奇妙的共时性的关系,形成一种虚拟的部落式观影氛围,让B站成为极具互动分享和二次创造的文化社区。B站目前也是众多网络热门词汇的发源地之一。</p><p>2019年4月22日,针对“后台源码泄露”一事,B站在今晚已经做出回应,其表示,经内部紧急核查,确认该部分代码属于较老的历史版本<span class="sup--normal" style="font-size: 12px; line-height: 0; position: relative; vertical-align: baseline; top: -0.5em; margin-left: 2px; color: rgb(51, 102, 204); cursor: pointer; padding: 0px 2px;">&nbsp;[1]</span><a class="sup-anchor" style="color: rgb(19, 110, 194); position: relative; top: -50px; font-size: 0px; line-height: 0;">&nbsp;</a>&nbsp;;5月29日,哔哩哔哩发布通知称,因弹幕系统技术升级,5月29日起至6月6日网站将暂时关闭弹幕功能</p><p><br/></p><p><br/></p><p>位置:</p><p><iframe src="./assets/ueditor/dialogs/map/show.html#center=116.404,39.915&zoom=10&width=530&height=340&markers=116.404,39.915&markerStyles=l,A" frameborder="0" width="534" height="344"></iframe></p>';

  title = 'platform';

  constructor(private sanitizer: DomSanitizer) {

  }


  transform(text: string) {
    return this.sanitizer.bypassSecurityTrustHtml(text);
  }
}

好了,这样就能成功显示我们编辑的内容了,剩下的样式调整就行了。

03-05 14:41