效果图
下载
npm i ckeditor5 @ckeditor/ckeditor5-vue
使用
<script setup>
import { computed, ref, onMounted } from "vue";
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
import {
Alignment,
Title,
Bookmark,
Code,
Strikethrough,
Autoformat,
ClassicEditor,
AutoImage,
Autosave,
BlockQuote,
Bold,
CodeBlock,
Essentials,
FontBackgroundColor,
FontColor,
FontFamily,
FontSize,
FullPage,
Heading,
HorizontalLine,
Image,
ImageBlock,
ImageCaption,
ImageInline,
ImageInsert,
ImageInsertViaUrl,
ImageResize,
ImageResizeHandles,
ImageStyle,
ImageTextAlternative,
ImageToolbar,
ImageUpload,
Indent,
IndentBlock,
Italic,
Link,
LinkImage,
List,
ListProperties,
Paragraph,
PasteFromOffice,
SimpleUploadAdapter,
TableLayout,
// SourceEditing,
SpecialCharacters,
SpecialCharactersArrows,
SpecialCharactersCurrency,
SpecialCharactersEssentials,
SpecialCharactersLatin,
SpecialCharactersMathematical,
SpecialCharactersText,
Subscript,
Superscript,
Table,
TableCaption,
TableCellProperties,
TableColumnResize,
TableProperties,
TableToolbar,
TextTransformation,
TodoList,
Underline,
ImageResizeEditing,
Base64UploadAdapter,
// Editor
} from "ckeditor5";
import "ckeditor5/ckeditor5.css";
import coreTranslations from "ckeditor5/translations/zh-cn.js";
const props = defineProps(["content"]); //初始化展示内容
const emits = defineEmits(["contentChange"]); //自动保存,提交数据给父级组件
const isLayoutReady = ref(false);
const editor = ClassicEditor;
const config = computed(() => {
if (!isLayoutReady.value) {
return null;
}
return {
translations: [coreTranslations],
toolbar: {
items: [
"undo",
"redo",
"|",
"heading",
"|",
"fontColor",
"fontBackgroundColor",
{
label: "文字大小/字体",
items: ["fontSize", "fontFamily"],
},
"Alignment",
"outdent",
"indent",
"|",
"bold",
"italic",
"underline",
{
label: "文本编辑",
items: ["superscript", "subscript", "strikethrough", "code"],
},
"|",
"blockQuote",
"link",
"bookmark",
"codeBlock",
"insertImage",
"insertTable",
"insertTableLayout",
"specialCharacters",
"|",
"bulletedList",
"numberedList",
"todoList",
"horizontalLine",
],
shouldNotGroupWhenFull: false,
},
plugins: [
ImageResizeEditing,
Base64UploadAdapter,
Title,
Code,
Bookmark,
Strikethrough,
Autoformat,
AutoImage,
Autosave,
BlockQuote,
Bold,
CodeBlock,
Essentials,
TableLayout,
FontBackgroundColor,
FontColor,
FontFamily,
FontSize,
Alignment,
FullPage,
Heading,
HorizontalLine,
Image,
ImageResizeHandles,
ImageBlock,
ImageCaption,
ImageInline,
ImageInsert,
ImageInsertViaUrl,
ImageResize,
ImageStyle,
ImageTextAlternative,
ImageToolbar,
ImageUpload,
Indent,
IndentBlock,
Italic,
Link,
LinkImage,
List,
ListProperties,
Paragraph,
PasteFromOffice,
SimpleUploadAdapter,
// SourceEditing,
SpecialCharacters,
SpecialCharactersArrows,
SpecialCharactersCurrency,
SpecialCharactersEssentials,
SpecialCharactersLatin,
SpecialCharactersMathematical,
SpecialCharactersText,
Subscript,
Superscript,
Table,
TableCaption,
TableCellProperties,
TableColumnResize,
TableProperties,
TableToolbar,
TextTransformation,
TodoList,
Underline,
],
fontFamily: {
supportAllValues: true,
},
fontSize: {
options: [10, 12, 14, "default", 18, 20, 22],
supportAllValues: true,
},
image: {
insert: {
integrations: ["upload"],
},
toolbar: [
"toggleImageCaption",
"imageTextAlternative",
"|",
"imageStyle:side",
"imageStyle:inline",
"imageStyle:wrapText",
"imageStyle:breakText",
"|",
"resizeImage",
"|",
"linkImage",
],
},
initialData: props.content,
licenseKey: "GPL",
autosave: {
save(editor) {
//函数内写自定义的存盘方法,下面是将内容提交给父级组件处理如何存盘
return new Promise((resolve) => {
emits("contentChange", editor.getData());
resolve(0);
});
},
},
list: {
properties: {
styles: true,
startIndex: true,
reversed: true,
},
},
// simpleUpload:{
// uploadUrl: '/image/upload' //写自己的图片上传路径
// },
link: {
addTargetToExternalLinks: true,
},
title: {
placeholder: "标题",
},
placeholder: "内容",
table: {
tableLayout: {
preferredExternalTableType: "content", // or 'layout'
},
contentToolbar: [
"tableColumn",
"tableRow",
"mergeTableCells",
"tableProperties",
"tableCellProperties",
],
},
};
});
onMounted(() => {
isLayoutReady.value = true;
});
const editorData = ref();
let Editor = null;
const onEditorReady = (editor) => (Editor = editor);
const subContent = () => {
const data = Editor.getData();
editorData.value = data;
};
</script>
<template>
<div v-if="editor && config">
<ckeditor
v-model="config.initialData"
:editor="editor"
:config="config"
@ready="onEditorReady"
/>
<button @click="subContent">发布</button>
</div>
<div v-html="editorData" class="ck-content"></div>
</template>
<style>
.ck .ck-powered-by {
display: none !important;
}
</style>
注意
LOGO
.ck .ck-powered-by
是为了隐藏logo
渲染必须的class
- 渲染的父级元素必须添加class
ck-content
- 否则渲染无效
<div class="ck-content"></div>