CKeditor 编辑器使用记录 (CKeditor 4 and 5 )

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39759115/article/details/85098832

CKeditor 编辑器

CKeditor 是一款可定制的适合开发人员使用的富文本编辑器

编辑器官网

CKeditor 4 使用

官网

自定义构建

构建页面

常用插件

Code tag
Easy Image
Find / Replace
Font Size and Family
Letter-spacing
Line Height
Page Break
Paragraph Indentation
Statistics of the number of characters
Stored automatically in the browser storage
Text Transform

CKeditor 5 使用

CDN 使用

div id="editor">
	<h2>Sample</h2>

	<p>This is an instance of the <a href="https://ckeditor.com/docs/ckeditor5/latest/builds/guides/overview.html#classic-editor">classic editor build</a>.</p>

	<figure class="image"><img src="../tests/manual/sample.jpg" alt="Autumn fields" /></figure>

	<p>You can use this sample to validate whether your <a href="https://ckeditor.com/docs/ckeditor5/latest/builds/guides/development/custom-builds.html">custom build</a> works fine.</p>
</div>

<script src="../build/ckeditor.js"></script>
<script>
	ClassicEditor.create(document.querySelector("#editor"), {
		// config
	})
		.then(editor => {
			window.editor = editor;
		})
		.catch(err => {
			console.error(err.stack);
		});

	
</script>

npm 使用

官方文档

安装

npm install --save @ckeditor/ckeditor5-vue @ckeditor/ckeditor5-build-classic

main.js引入

import Vue from 'vue';
import CKEditor from '@ckeditor/ckeditor5-vue';

Vue.use(CKEditor);

单文件组件使用

<template>
    <div id="app">
        <ckeditor :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
    </div>
</template>

<script>
    import ClassicEditor from '@ckeditor/ckeditor5-build-classic';

    export default {
        name: 'app',
        data() {
            return {
                editor: ClassicEditor,
                editorData: '<p>Content of the editor.</p>',
                editorConfig: {
                    // The configuration of the editor.
                }
            };
        }
    }
</script>

自定义图片上传

自定义图片上传插件必需架构

// 自定义文件上传
	class MyUploadAdapter {
		constructor(loader, url) {
			// 上传期间要使用的 FileLoader 实例。 
			this.loader = loader;

			// 服务器后端的上传URL。 这 是XMLHttpRequest 将图像数据发送到的地址。
			this.url = url;
		}

		// 上传
		upload() {
			return new Promise((resolve, reject) => {
			    // 自定义上传逻辑。。。 假设 response 为响应数据
			
			    // 上传失败时必须调用 reject() 函数
				if (!response || response.error) {
				   return reject(errorMessage);
				}
				
				// 如果上传成功,请调用 resolve() 并传入至少包含 {default: URL} 的对象,该 URL 为上传图像在服务器上的地址。
				resolve({
				   default: response.url // // 此 URL 将用于在内容中显示图像。 
				});
			});
		}

		// 中止上传
		abort() {
			if (this.xhr) {
				this.xhr.abort();
			}
		}
	}
	

自定义图片上传插件

    // 自定义文件上传
	class MyUploadAdapter {
		constructor(loader, url) {
			// 上传期间要使用的 FileLoader 实例。 
			this.loader = loader;

			// 服务器后端的上传URL。 这 是XMLHttpRequest 将图像数据发送到的地址。
			this.url = url;
		}

		// 开始上传
		upload() {
			return new Promise((resolve, reject) => {
				this._initRequest();
				this._initListeners(resolve, reject);
				this._sendRequest();
			});
		}

		// 中止上传
		abort() {
			if (this.xhr) {
				this.xhr.abort();
			}
		}

		// 使用传递给构造函数的 URL 初始化 XMLHttpRequest 对象。
		_initRequest() {
			const xhr = (this.xhr = new XMLHttpRequest());

			// 请注意,您的请求可能会有所不同。 由您和您的编辑器集成来选择正确的通信渠道。 此示例使用带有JSON的POST请求作为数据结构,但您的配置可能不同。
			xhr.open("POST", this.url, true);
			xhr.responseType = "json";
		}

		// 初始化 XMLHttpRequest 侦听器。
		_initListeners(resolve, reject) {
			const xhr = this.xhr;
			const loader = this.loader;
			const genericErrorText = "Couldn't upload file:" + ` ${loader.file.name}.`;

			xhr.addEventListener("error", () => reject(genericErrorText));
			xhr.addEventListener("abort", () => reject());
			xhr.addEventListener("load", () => {
				const response = xhr.response;

			    // 上传失败时必须调用 reject() 函数。
				if (!response || response.error) {
				   return reject(response && response.error ? response.error.message : genericErrorText);
				}

				// 如果上传成功,请使用至少包含 {default: URL} 的对象,该 URL 为上传图像在服务器上的地址。
				resolve({
				   default: response.url // // 此URL将用于在内容中显示图像。 
				});
			
			});

			// 支持上传进度。 FileLoader 具有 uploadTotal 和 uploaded 属性,如果使用它们,在编辑器的界面中将会显示上传的进度条。
			if (xhr.upload) {
				xhr.upload.addEventListener("progress", evt => {
					if (evt.lengthComputable) {
						loader.uploadTotal = evt.total;
						loader.uploaded = evt.loaded;
					}
				});
			}
		}
		// 准备数据并发送请求。
		_sendRequest() {
			// 准备表单数据。
			const data = new FormData();
			data.append("upload", this.loader.file);

			// 发送请求。
			this.xhr.send(data);
		}
	}

对接插件,以便在初始化编辑器时使用

	// 对接插件
	function MyCustomUploadAdapterPlugin(editor) {
		editor.plugins.get("FileRepository").createUploadAdapter = loader => {
			// Configure the URL to the upload script in your back-end here!
			return new MyUploadAdapter(loader, "https://img.sqydt.darongshutech.com/");
		};
	}

初始化编辑器,并加载自定义图片上传功能

	// 创建编辑器实例,并加载自定义图片上传功能
	ClassicEditor.create(document.querySelector("#editor"), {
		extraPlugins: [MyCustomUploadAdapterPlugin]
	})
	.then(editor => {
		window.editor = editor;
	})
	.catch(err => {
		console.error(err.stack);
	});

自定义图片上传(七牛云)

import axios from "axios";
window.axios = axios;
function MyCustomUploadAdapterPlugin(editor) {
  editor.plugins.get("FileRepository").createUploadAdapter = loader => {
    // Configure the URL to the upload script in your back-end here!
    return new MyUploadAdapter(loader, 10, "api/public/getUploadToken");
  };
}
// 自定义七妞云上传
class MyUploadAdapter {
  constructor(loader, maxSize, getToken) {
    // 上传期间要使用的 FileLoader 实例。
    this.loader = loader;

    // 图片大小限制
    this.maxSize = maxSize;

    // 获取上传七牛云token,接口地址
    this.upTokenUrl = getToken;

    // 允许上传的图片格式
    this.acceptString = ".jpg,.jpeg,.png,.gif,.JPG,.JPEG,.PNG,.GIF";

    // 七牛云上传地址
    this.qiniuUrl = "http://up.qiniu.com";

    // 文件存放地址
    this.baseUrl = "https://img.rmsq.com/";
  }

  // 上传
  upload() {
    return new Promise((resolve, reject) => {
      this._uploadFile(resolve, reject);
    });
  }

  // 中止上传
  abort() {
    if (this.xhr) {
      this.xhr.abort();
    }
  }
  
  _uploadFile(resolveCK, rejectCK) {
    const file = this.loader.file;
    const isLt = file.size / 1024 / 1024 < this.maxSize;

    // const fileExt = _file.type.split("/")[1]; // .mp3 在火狐和ie 读取的格式为 mpeg
    const lastIndex = file.name.lastIndexOf(".");
    const fileExt = file.name.substring(+lastIndex + 1); // 上传的文件的格式
    const allAcceptArr = this.acceptString.split(","); // 允许上传的格式

    if (!this._isInArr("." + fileExt, allAcceptArr)) {
      console.error("请上传格式为" + this.acceptString + "的文件!");
      return false;
    }

    if (this.maxSize && !isLt) {
      console.error("请上传小于" + this.maxSize + "MB的文件!");
      return false;
    }

    // 使用FormData对象上传文件
    var formData = new FormData();
    formData.append("file", file);

    // 获取七牛云TOKEN
    this._getQiniuToken(fileExt).then(resData => {
      formData.append("token", resData.data.token);
      formData.append("key", resData.data.key);

      // 上传文件至七牛云
      this._requestQiniu(formData)
        .then(response => {
          // 如果上传成功,请调用 resolve() 并传入至少包含 {default: URL} 的对象,该 URL 为上传图像在服务器上的地址。
          resolveCK({
            default: this.baseUrl + response.key // // 此 URL 将用于在内容中显示图像。
          });
        })
        .catch(err => {
          // 上传失败
          rejectCK(err);
        });
    });
  }

  _isInArr(_val, _arr) {
    return _arr.some(item => {
      if (item === _val) {
        return true;
      }
    });
  }

  _getQiniuToken(fileExt) {
    return new Promise((resolve, reject) => {
      axios.get(this.upTokenUrl, { params: { file_ext: fileExt } }).then(res => {
        if (res && res.data && res.data.code === 2000) {
          resolve(res.data);
        } else {
          console.error(res.data.msg);
        }
      });
    });
  }

  _requestQiniu(formData) {
    const config = {
      headers: { "Content-Type": "multipart/form-data" },
      onUploadProgress: progressEvent => {
        // 上传进度
        this.loader.uploadTotal = progressEvent.total;
        this.loader.uploaded = progressEvent.loaded;
      }
    };
    return new Promise((resolve, reject) => {
      axios.post(this.qiniuUrl, formData, config).then(res => {
        if (res && res.status == 200) {
          resolve(res.data);
        } else {
          console.error(res.data.msg);
        }
      });
    });
  }
}

使用

    initEditor() {
      ClassicEditor.create(document.querySelector("#sqbCkEditor"), {
        // The configuration of the editor.
        language: "zh-cn",
        toolbar: ["undo", "redo", "|", "heading", "|", "fontSize", "bold", "italic", "link", "bulletedList", "numberedList", "blockQuote", "imageUpload", "imageStyle:full", "imageStyle:side"],
        extraPlugins: [MyCustomUploadAdapterPlugin] // 使用自定义上传图片插件
        // ckfinder: {
        //   uploadUrl: "http://up.qiniu.com"
        // }
      })
        .then(editor => {
          editor.setData("<p>默认内容</p>");
          this.ckEditor = editor;
        })
        .catch(msg => {
          console.log("msg", msg);
        });
    },

自定义构建

文档描述

基本操作

汉化

  import "@ckeditor/ckeditor5-build-classic/build/translations/zh-cn.js";

  ClassicEditor.create(document.querySelector("#sqbCkEditor"), {
    // The configuration of the editor.
    language: "zh-cn",
    // ckfinder: {
    //   uploadUrl: "http://up.qiniu.com"
    // }
  })
    .then(editor => {
      console.log("asd", editor);
    })
    .catch(msg => {
      console.log("msg", msg);
    });

设置内容

this.ckEditor.setData("<p>内容</p>");

获取编辑器的内容,HTML

const editorContent = this.ckEditor.getData();

获取编辑器的内容,纯文本

this.ckEditor.getData().replace(/<[^>]*>/gi, "");

获取编辑器的内容,过滤图片

this.ckEditor.getData().replace(/<img.*?(?:>|\/>)/gi, "");

获取编辑器的内容,过滤上传的图片

this.ckEditor.getData().replace(/<figure[^>]*.*?>.*?<\/figure>/gi, "");

获取可用工具栏配置

Array.from(this.formControl.ckEditor.ui.componentFactory.names())

还原编辑器样式

  1. 获取编辑器内的DOM(未经转换),在加载了编辑器 css 的项目中可还原
const _editorDom = this.ckEditor.editing.view.domRoots.get("main").outerHTML;
  1. 通过编辑器还原

与 CKEditor 4 不同,CKEditor 5 实现了自定义数据模型。这意味着加载到编辑器中的每个内容都需要转换为该模型,然后再渲染回视图。
每种内容都必须由某些功能处理。该功能定义了HTML(视图)和编辑器模型之间的双向转换。

    <div id="showContent"></div>

    ClassicEditor.create(document.querySelector('#showContent')).then(editor => {
      editor.isReadOnly = true; // 将编辑器设为只读
      editor.setData(editorContent) // 显示内容 editorContent
    }).catch(msg => {
      console.error(msg);
    });

猜你喜欢

转载自blog.csdn.net/qq_39759115/article/details/85098832