第一种:quill-editor
使用如下:
- 首先引入:
import VueQuillEditor from 'vue-quill-editor';
// require styles
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
Vue.use(VueQuillEditor, /* { default global options } */)
- 页面使用,你还可以进行配置
<quill-editor v-model="content" ref="myQuillEditor" :options="editorOption"></quill-editor>
//quill编辑器的字体
import * as Quill from 'quill' //引入编辑器
var fonts = ['SimSun', 'SimHei','Microsoft-YaHei','KaiTi','FangSong','Arial','Times-New-Roman','sans-serif'];
var Font = Quill.import('formats/font');
Font.whitelist = fonts; //将字体加入到白名单
Quill.register(Font, true);
//quill图片可拖拽上传
import { ImageDrop } from 'quill-image-drop-module';
Quill.register('modules/imageDrop', ImageDrop);
//quill图片可拖拽改变大小
import { ImageResize } from 'quill-image-resize-module';
// 富文本编辑器配置项:
editorOption: {
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'], //加粗,斜体,下划线,删除线
['blockquote', 'code-block'], //引用,代码块
[{ 'header': 1 }, { 'header': 2 }], // 标题,键值对的形式;1、2表示字体大小
[{ 'list': 'ordered'}, { 'list': 'bullet' }], //列表
[{ 'script': 'sub'}, { 'script': 'super' }], // 上下标
[{ 'indent': '-1'}, { 'indent': '+1' }], // 缩进
[{ 'direction': 'rtl' }], // 文本方向
[{ 'size': ['small', false, 'large', 'huge'] }], // 字体大小
[{ 'header': [1, 2, 3, 4, 5, 6, false] }], //几级标题
[{ 'color': [] }, { 'background': [] }], // 字体颜色,字体背景颜色
[{ 'font': fonts }], //字体
[{ 'align': [] }], //对齐方式
['clean'], //清除字体样式
['image','video'] //上传图片、上传视频
],
imageDrop: true
},
// theme:'bubble'
theme:'snow',
}
- 效果展示
第二种:wangeditor
- 引入,当然还可以引入表情json文件:
import E from "wangeditor";
// 引入表情:
import expression from "../../assets/expression.json";
import expression02 from "../../assets/expression02.json";
- 页面使用
<div id="wangeditor">
<div ref="editorElem"></div>
</div>
mounted() {
// this.editor.txt.html('<p>121212</p>');
this.editor = new E(this.$refs.editorElem);
// 编辑器的事件,每次改变会获取其html内容
this.editor.customConfig.onchange = html => {
this.editorContent = html;
};
this.editor.customConfig.menus = [
// 菜单配置
"head", // 标题
"bold", // 粗体
"fontSize", // 字号
"fontName", // 字体
"italic", // 斜体
"underline", // 下划线
"strikeThrough", // 删除线
"foreColor", // 文字颜色
"backColor", // 背景颜色
"link", // 插入链接
"list", // 列表
"justify", // 对齐方式
"quote", // 引用
"emoticon", // 表情
"image", // 插入图片
"table", // 表格
"code", // 插入代码
"undo", // 撤销
"redo" // 重复
];
// 配置颜色:
this.editor.customConfig.colors = [
"#000000",
"#eeece0",
"#1c487f",
"#4d80bf",
"#c24f4a",
"#8baa4a",
"#7b5ba1",
"#46acc8",
"#f9963b",
"#ffffff",
"rgba(255, 0, 0, 0.3)"
];
// 配置字体:
this.editor.customConfig.fontNames = [
"宋体",
"微软雅黑",
"Arial",
"Tahoma",
"Verdana",
"华文行楷"
];
// 表情面板可以有多个 tab ,因此要配置成一个数组。数组每个元素代表一个 tab 的配置
this.editor.customConfig.emotions = [
{
// tab 的标题
title: "默认",
// type -> 'emoji' / 'image'
type: "image",
// content -> 数组
content: [
{
alt: "[坏笑]",
src:
"http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/50/pcmoren_huaixiao_org.png"
},
{
alt: "[舔屏]",
src:
"http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/40/pcmoren_tian_org.png"
}
]
},
{
title: "引入",
type: "image",
content: this.newExpression
},
{
title: "引入2",
type: "image",
content: this.newExpression02
},
{
// tab 的标题
title: "emoji",
// type -> 'emoji' / 'image'
type: "emoji",
// content -> 数组
content: ["😀", "😃", "😄", "😁", "😆"]
}
];
this.editor.create(); // 创建富文本实例
}
- 效果展示:
第三种:tinymce
- 引入:
import tinymce from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/themes/silver'
// 编辑器插件plugins
import 'tinymce/plugins/image'// 插入上传图片插件
import 'tinymce/plugins/media'// 插入视频插件
import 'tinymce/plugins/table'// 插入表格插件
import 'tinymce/plugins/lists'// 列表插件
import 'tinymce/plugins/wordcount'// 字数统计插件
- 页面使用:
<div class="tinymce-editor">
<editor v-model="myValue"
:init="init"
:disabled="disabled"
@onClick="onClick">
</editor>
</div>
plugins: {
type: [String, Array],
default: 'lists image media table wordcount'
},
toolbar: {
type: [String, Array],
default: 'undo redo | formatselect | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat'
},
init: {
language_url: `${this.baseUrl}/tinymce/langs/zh_CN.js`,
language: 'zh_CN',
skin_url: `${this.baseUrl}/tinymce/skins/ui/oxide`,
content_css: `${this.baseUrl}/tinymce/skins/content/default/content.css`,
// skin_url: `${this.baseUrl}/tinymce/skins/ui/oxide-dark`, // 暗色系
// content_css: `${this.baseUrl}/tinymce/skins/content/dark/content.css`, // 暗色系
height: 300,
plugins: this.plugins,
toolbar: this.toolbar,
branding: false,
menubar: false,
// 此处为图片上传处理函数,这个直接用了base64的图片形式上传图片,
// 如需ajax上传可参考https://www.tiny.cloud/docs/configure/file-image-upload/#images_upload_handler
images_upload_handler: (blobInfo, success, failure) => {
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
success(img)
}
},
- 效果
第四种:kindeditor
- 创建一个kindeditor.vue,代码如下:
<template>
<div class="kindeditor">
<textarea :id="id" name="content" v-model="outContent"></textarea>
</div>
</template>
<script>
import "kindeditor/kindeditor-all.js";
import "kindeditor/lang/zh-CN.js";
import "kindeditor/themes/default/default.css";
export default {
name: "kindeditor",
data() {
return {
editor: null,
outContent: this.content
};
},
props: {
content: {
type: String,
default: ""
},
id: {
type: String,
required: true
},
width: {
type: String
},
height: {
type: String
},
minWidth: {
type: Number,
default: 650
},
minHeight: {
type: Number,
default: 100
},
items: {
type: Array,
default: function() {
return [
"source",
"|",
"undo",
"redo",
"|",
"preview",
"print",
"template",
"code",
"cut",
"copy",
"paste",
"plainpaste",
"wordpaste",
"|",
"justifyleft",
"justifycenter",
"justifyright",
"justifyfull",
"insertorderedlist",
"insertunorderedlist",
"indent",
"outdent",
"subscript",
"superscript",
"clearhtml",
"quickformat",
"selectall",
"|",
"fullscreen",
"/",
"formatblock",
"fontname",
"fontsize",
"|",
"forecolor",
"hilitecolor",
"bold",
"italic",
"underline",
"strikethrough",
"lineheight",
"removeformat",
"|",
"image",
"multiimage",
"flash",
"media",
"insertfile",
"table",
"hr",
"emoticons",
"baidumap",
"pagebreak",
"anchor",
"link",
"unlink",
"|",
"about"
];
}
},
noDisableItems: {
type: Array,
default: function() {
return ["source", "fullscreen"];
}
},
filterMode: {
type: Boolean,
default: true
},
htmlTags: {
type: Object,
default: function() {
return {
font: ["color", "size", "face", ".background-color"],
span: ["style"],
div: ["class", "align", "style"],
table: [
"class",
"border",
"cellspacing",
"cellpadding",
"width",
"height",
"align",
"style"
],
"td,th": [
"class",
"align",
"valign",
"width",
"height",
"colspan",
"rowspan",
"bgcolor",
"style"
],
a: ["class", "href", "target", "name", "style"],
embed: [
"src",
"width",
"height",
"type",
"loop",
"autostart",
"quality",
"style",
"align",
"allowscriptaccess",
"/"
],
img: [
"src",
"width",
"height",
"border",
"alt",
"title",
"align",
"style",
"/"
],
hr: ["class", "/"],
br: ["/"],
"p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6": ["align", "style"],
"tbody,tr,strong,b,sub,sup,em,i,u,strike": []
};
}
},
wellFormatMode: {
type: Boolean,
default: true
},
resizeType: {
type: Number,
default: 2
},
themeType: {
type: String,
default: "default"
},
langType: {
type: String,
default: "zh-CN"
},
designMode: {
type: Boolean,
default: true
},
fullscreenMode: {
type: Boolean,
default: false
},
basePath: {
type: String
},
themesPath: {
type: String
},
pluginsPath: {
type: String,
default: ""
},
langPath: {
type: String
},
minChangeSize: {
type: Number,
default: 5
},
loadStyleMode: {
type: Boolean,
default: true
},
urlType: {
type: String,
default: ""
},
newlineTag: {
type: String,
default: "p"
},
pasteType: {
type: Number,
default: 2
},
dialogAlignType: {
type: String,
default: "page"
},
shadowMode: {
type: Boolean,
default: true
},
zIndex: {
type: Number,
default: 811213
},
useContextmenu: {
type: Boolean,
default: true
},
syncType: {
type: String,
default: "form"
},
indentChar: {
type: String,
default: "\t"
},
cssPath: {
type: [String, Array]
},
cssData: {
type: String
},
bodyClass: {
type: String,
default: "ke-content"
},
colorTable: {
type: Array
},
afterCreate: {
type: Function
},
afterChange: {
type: Function
},
afterTab: {
type: Function
},
afterFocus: {
type: Function
},
afterBlur: {
type: Function
},
afterUpload: {
type: Function
},
uploadJson: {
type: String
},
fileManagerJson: {
type: Function
},
allowPreviewEmoticons: {
type: Boolean,
default: true
},
allowImageUpload: {
type: Boolean,
default: true
},
allowFlashUpload: {
type: Boolean,
default: true
},
allowMediaUpload: {
type: Boolean,
default: true
},
allowFileUpload: {
type: Boolean,
default: true
},
allowFileManager: {
type: Boolean,
default: false
},
fontSizeTable: {
type: Array,
default: function() {
return ["9px", "10px", "12px", "14px", "16px", "18px", "24px", "32px"];
}
},
imageTabIndex: {
type: Number,
default: 0
},
formatUploadUrl: {
type: Boolean,
default: true
},
fullscreenShortcut: {
type: Boolean,
default: false
},
extraFileUploadParams: {
type: Array,
default: function() {
return [];
}
},
filePostName: {
type: String,
default: "imgFile"
},
fillDescAfterUploadImage: {
type: Boolean,
default: false
},
afterSelectFile: {
type: Function
},
pagebreakHtml: {
type: String,
default: "<hr style=”page-break-after: always;” class=”ke-pagebreak” />"
},
allowImageRemote: {
type: Boolean,
default: true
},
autoHeightMode: {
type: Boolean,
default: false
},
fixToolBar: {
type: Boolean,
default: false
},
tabIndex: {
type: Number
}
},
watch: {
content(val) {
this.editor && val !== this.outContent && this.editor.html(val);
},
outContent(val) {
this.$emit("update:content", val);
this.$emit("on-content-change", val);
}
},
mounted() {
var _this = this;
_this.editor = window.KindEditor.create("#" + this.id, {
width: _this.width,
height: _this.height,
minWidth: _this.minWidth,
minHeight: _this.minHeight,
items: _this.items,
noDisableItems: _this.noDisableItems,
filterMode: _this.filterMode,
htmlTags: _this.htmlTags,
wellFormatMode: _this.wellFormatMode,
resizeType: _this.resizeType,
themeType: _this.themeType,
langType: _this.langType,
designMode: _this.designMode,
fullscreenMode: _this.fullscreenMode,
basePath: _this.basePath,
themesPath: _this.cssPath,
pluginsPath: _this.pluginsPath,
langPath: _this.langPath,
minChangeSize: _this.minChangeSize,
loadStyleMode: _this.loadStyleMode,
urlType: _this.urlType,
newlineTag: _this.newlineTag,
pasteType: _this.pasteType,
dialogAlignType: _this.dialogAlignType,
shadowMode: _this.shadowMode,
zIndex: _this.zIndex,
useContextmenu: _this.useContextmenu,
syncType: _this.syncType,
indentChar: _this.indentChar,
cssPath: _this.cssPath,
cssData: _this.cssData,
bodyClass: _this.bodyClass,
colorTable: _this.colorTable,
afterCreate: _this.afterCreate,
afterChange: function() {
_this.afterChange;
_this.outContent = this.html();
},
afterTab: _this.afterTab,
afterFocus: _this.afterFocus,
afterBlur: _this.afterBlur,
afterUpload: _this.afterUpload,
uploadJson: _this.uploadJson,
fileManagerJson: _this.fileManagerJson,
allowPreviewEmoticons: _this.allowPreviewEmoticons,
allowImageUpload: _this.allowImageUpload,
allowFlashUpload: _this.allowFlashUpload,
allowMediaUpload: _this.allowMediaUpload,
allowFileUpload: _this.allowFileUpload,
allowFileManager: _this.allowFileManager,
fontSizeTable: _this.fontSizeTable,
imageTabIndex: _this.imageTabIndex,
formatUploadUrl: _this.formatUploadUrl,
fullscreenShortcut: _this.fullscreenShortcut,
extraFileUploadParams: _this.extraFileUploadParams,
filePostName: _this.filePostName,
fillDescAfterUploadImage: _this.fillDescAfterUploadImage,
afterSelectFile: _this.afterSelectFile,
pagebreakHtml: _this.pagebreakHtml,
allowImageRemote: _this.allowImageRemote,
autoHeightMode: _this.autoHeightMode,
fixToolBar: _this.fixToolBar,
tabIndex: _this.tabIndex
});
}
};
</script>
<style>
</style>
- 页面使用:
<editor
id="editor_id"
height="500px"
width="700px"
:content.sync="editorText"
:afterChange="afterChange()"
:loadStyleMode="false"
@on-content-change="onContentChange"
></editor>
<script>
import editor from "./kindEditorCom/kindEditor.vue";
export default {
name: "app",
components: {
editor
},
data() {
return {
editorText: "直接初始化值", // 双向同步的变量
editorTextCopy: "" // content-change 事件回掉改变的对象
};
},
methods: {
onContentChange(val) {
this.editorTextCopy = val;
console.log(this.editorTextCopy);
},
afterChange() {}
}
};
</script>
- 效果:
第五种 UEditor
- 使用vue-ueditor-wrap的步骤: 1.下载UEditor,如果是vue2.x项目,需要放在static文件夹下面,如果你使用的是 vue-cli 3.x,可以把 UEditor 文件夹放入项目的 public 目录下
2.引入VueUeditorWrap组件
- import VueUeditorWrap from 'vue-ueditor-wrap' // ES6 Module
- 或者 const VueUeditorWrap = require('vue-ueditor-wrap') //CommonJS
3.注册组件
- components: { VueUeditorWrap }
- 或者在 main.js 里将它注册为全局组件 Vue.component('vue-ueditor-wrap', VueUeditorWrap)
4.可以使用了
<vue-ueditor-wrap v-model="msg" :config="myConfig"></vue-ueditor-wrap>
myConfig: {
// 编辑器不自动被内容撑高
autoHeightEnabled: false,
// 初始容器高度
initialFrameHeight: 240,
// 初始容器宽度
initialFrameWidth: "100%",
// 上传文件接口(这个地址是我为了方便各位体验文件上传功能搭建的临时接口,请勿在生产环境使用!!!)
serverUrl: "http://35.201.165.105:8000/controller.php",
// UEditor 资源文件的存放路径,如果你使用的是 vue-cli 生成的项目,通常不需要设置该选项,vue-ueditor-wrap 会自动处理常见的情况,如果需要特殊配置,参考下方的常见问题2
UEDITOR_HOME_URL: "/UEditor/"
}
- 效果: