关键词:
近期项目中需要使用富文本编辑器,开始想到的富文本编辑器是百度的UEditor,UEditor功能齐全、插件多,但是图片只能上传到本地服务器,如果需要上传到其他服务器需要改动源码,而且是PHP、JSP、ASP、.Net版本,同时UEditor体积过大压缩包有3.3M(jsp版本),加载速度慢。实际项目中并不需要那么多的功能,只需要基础的操作:字体、字体大小、标题、段落、图片上传、视频上传、居中对齐等;所以我们只需要一个轻量级的富文本编辑器就行,推荐使用Quill和TinyMCE,Quill和TinyMCE都是轻量级,插件多,功能强,编辑能力优秀,界面好看。选择Quill的原因是它所有能看到的,不能看到的功能统统都是一个一个独立的模块,全部都是可以替换的,可以自定义编辑。
站在巨人vue-quill-editor的肩膀上进行Quill editor的自定义图片/视频上传(Element UI + OSS)、字体、字体大小、标题、段落等封装。先看效果再来编码:
<template>
<div>
<quill-editor ref="myTextEditor"
v-model="contentValue"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@ready="onEditorReady($event)"
@change="onEditorChange($event)"
class="cfpa-quill-editor" :style=" height: quillEditorHeight + 'px' ">
<div id="toolbar" slot="toolbar">
<!-- Add a bold button -->
<button class="ql-bold" title="加粗">Bold</button>
<button class="ql-italic" title="斜体">Italic</button>
<button class="ql-underline" title="下划线">underline</button>
<button class="ql-strike" title="删除线">strike</button>
<button class="ql-blockquote" title="引用"></button>
<button class="ql-code-block" title="代码"></button>
<button class="ql-header" value="1" title="标题1"></button>
<button class="ql-header" value="2" title="标题2"></button>
<!--Add list -->
<button class="ql-list" value="ordered" title="有序列表"></button>
<button class="ql-list" value="bullet" title="无序列表"></button>
<!-- Add font size dropdown -->
<select class="ql-header" title="段落格式">
<option selected>段落</option>
<option value="1">标题1</option>
<option value="2">标题2</option>
<option value="3">标题3</option>
<option value="4">标题4</option>
<option value="5">标题5</option>
<option value="6">标题6</option>
</select>
<select class="ql-size" title="字体大小">
<option value="10px">10px</option>
<option value="12px">12px</option>
<option value="14px">14px</option>
<option value="16px" selected>16px</option>
<option value="18px">18px</option>
<option value="20px">20px</option>
</select>
<select class="ql-font" title="字体">
<option value="SimSun" selected="selected"></option>
<option value="SimHei"></option>
<option value="Microsoft-YaHei"></option>
<option value="KaiTi"></option>
<option value="FangSong"></option>
<option value="Arial"></option>
<!-- <option value="Times-New-Roman"></option>
<option value="sans-serif"></option> -->
</select>
<!-- Add subscript and superscript buttons -->
<select class="ql-color" value="color" title="字体颜色"></select>
<select class="ql-background" value="background" title="背景颜色"></select>
<select class="ql-align" value="align" title="对齐"></select>
<button class="ql-clean" title="还原"></button>
<button class="ql-link" title="超链接"></button>
<!-- You can also add your own -->
<button id="custom-button" @click.prevent="fnOpenUploadImage" title="图片"><i class="iconfont icon-tupian"></i></button>
<button id="custom-button" @click.prevent="fnOpenUploadVideo" title="视频"><i class="iconfont icon-video2"></i></button>
</div>
</quill-editor>
<div :style="wordCount" v-if="wordCount" class="cfpa-quill-wordCount">
<div class="cfpa-quill-wordCount-text">当前已经输入<span style="color: red">contentLength</span>个字符</div>
</div>
<el-dialog :title="title" width="30%" :visible.sync="dialogFnOpenUpload" :close-on-click-modal="false">
<file-upload :accept="accept" :data_extra="data_extra" @fnUploadSucess="fnUploadSucess" @fnCloseDialog="dialogFnOpenUpload = false" ref="fileUpload"></file-upload>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogFnOpenUpload = false">取 消</el-button>
<el-button type="primary" @click="fnOpenUploadSubmit">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import FileUpload from '@/components/briefFileUpload'
import config from '@/config'
import Quill, quillEditor from 'vue-quill-editor'
// 图片可收缩
import ImageDrop from 'quill-image-drop-module'
import ImageResize from 'quill-image-resize-module'
Quill.register('modules/imageDrop', ImageDrop)
Quill.register('modules/imageResize', ImageResize)
// 自定义字体大小
let Size = Quill.import('attributors/style/size')
Size.whitelist = ['10px', '12px', '14px', '16px', '18px', '20px']
Quill.register(Size, true)
// 自定义字体类型
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)
export default
name: 'editor',
components:
quillEditor,
FileUpload
,
props:
value:
type: String,
default: ''
,
editorHeight:
type: Number,
default: 355
,
editorWordCount:
type: Number,
default: 0
,
data ()
return
contentValue: '',
preContent: '',
dialogFnOpenUpload: false,
accept: '',
uploadType: 'image',
editorOption:
modules:
toolbar: '#toolbar',
history:
delay: 1000,
maxStack: 50,
userOnly: false
,
imageDrop: true,
imageResize:
displayStyles:
backgroundColor: 'black',
border: 'none',
color: 'white'
,
modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]
,
placeholder: '请编写内容...'
,
data_extra:
parentId: 0,
fileName: ''
,
contentLength: 0,
wordCount: '',
title: '添加图片',
quillEditorHeight: 300
,
computed:
editor ()
return this.$refs.myTextEditor.quill
,
methods:
/**
* @description [onEditorBlur 失去焦点]
* @author zoumiao
* @param Object editor 返回的quill对象
* @return null [没有返回]
*/
onEditorBlur (editor)
this.$emit('editorBlur')
,
/**
* @description [onEditorFocus 获取焦点]
* @author zoumiao
* @param Object editor 返回的quill对象
* @return null [没有返回]
*/
onEditorFocus (editor)
this.$emit('editorFocus')
,
/**
* @description [onEditorReady 可以输入]
* @author zoumiao
* @param Object editor 返回的quill对象
* @return null [没有返回]
*/
onEditorReady (editor)
,
/**
* @description [onEditorChange 输入文本改变事件]
* @author zoumiao
* @param Object editor 返回的编辑对象html, text, quill
* @return null [没有返回]
*/
onEditorChange (editor)
let html = editor.html
this.preContent = html
this.$emit('input', html)
this.contentLength = this._.trim(editor.text).length
,
/**
* @description [fnOpenUploadImage 上传图片]
* @author zoumiao
* @return null [没有返回]
*/
fnOpenUploadImage ()
this.uploadType = 'image'
this.accept = config.accept.image
this.title = '添加图片'
this.dialogFnOpenUpload = true
,
/**
* @description [fnOpenUploadVideo 上传视频]
* @author zoumiao
* @return null [没有返回]
*/
fnOpenUploadVideo ()
this.uploadType = 'video'
this.accept = config.accept.video
this.title = '添加视频'
this.dialogFnOpenUpload = true
,
/**
* [fnOpenUploadSubmit 提交上传文件]
* @author zoumiao
* @return null [没有返回]
*/
async fnOpenUploadSubmit ()
await this.$refs.fileUpload.$refs.upload.submit()
,
/**
* [fnUploadSucess 上传文件成功]
* @author zoumiao
* @param Array uploadFileUrlList [上传文件返回的url]
* @return null [没有返回]
*/
fnUploadSucess (uploadFileUrlList)
this.editor.focus()
for (let url of uploadFileUrlList)
this.editor.insertEmbed(this.editor.getSelection().index, this.uploadType, url)
,
created ()
this.quillEditorHeight = document.body.clientHeight - this.editorHeight
this.contentValue = this.value
this.contentLength = this.editorWordCount || 0
,
mounted ()
let toolbar = document.querySelector('div.ql-toolbar.ql-snow')
if (toolbar)
let toolbarHeight = toolbar.offsetHeight
this.wordCount =
'top': `$toolbarHeightpx`
return
this.wordCount =
'top': '42px'
,
watch:
// Watch content change
value (newVal, oldVal)
if (newVal && newVal !== this.preContent)
this.preContent = newVal
this.contentValue = newVal
else if (!newVal)
this.contentValue = ''
</script>
<style lang="scss">
.cfpa-quill-editor
line-height: 24px;
.ql-snow
background-color: #ffffff;
.cfpa-quill-wordCount
background-color: #ffffff;
position: relative;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
line-height: 20px;
font-size: 12px;
.cfpa-quill-wordCount-text
text-align: right;
margin-right: 10px;
color: #aaa;
</style>
1、自定义字体、字体大小、段落
<div id="toolbar" slot="toolbar">
<select class="ql-header" title="段落格式">
<option selected>段落</option>
<option value="1">标题1</option>
<option value="2">标题2</option>
<option value="3">标题3</option>
<option value="4">标题4</option>
<option value="5">标题5</option>
<option value="6">标题6</option>
</select>
<select class="ql-size" title="字体大小">
<option value="10px">10px</option>
<option value="12px">12px</option>
<option value="14px">14px</option>
<option value="16px" selected>16px</option>
<option value="18px">18px</option>
<option value="20px">20px</option>
</select>
<select class="ql-font" title="字体">
<option value="SimSun" selected="selected"></option>
<option value="SimHei"></option>
<option value="Microsoft-YaHei"></option>
<option value="KaiTi"></option>
<option value="FangSong"></option>
<option value="Arial"></option>
<!-- <option value="Times-New-Roman"></option>
<option value="sans-serif"></option> -->
</select>
</div>
在Quill初始化之前进行注册:
// 自定义字体大小
let Size = Quill.import('attributors/style/size')
Size.whitelist = ['10px', '12px', '14px', '16px', '18px', '20px']
Quill.register(Size, true)
// 自定义字体类型
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编辑器之前引入quill.css,可以在App.vue或者main.js中引入。
.ql-snow .ql-picker.ql-size,
.ql-snow .ql-picker.ql-header
width: 75px !important;
.ql-snow .ql-picker.ql-font
width: 80px !important;
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="10px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="10px"]::before
content: '10px';
font-size: 10px;
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before
content: '12px';
font-size: 12px;
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before
content: '14px';
font-size: 14px;
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before
content: '16px';
font-size: 16px;
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before
content: '18px';
font-size: 18px;
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before
content: '20px';
font-size: 20px;
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before
content: "宋体";
font-family: "SimSun";
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before
content: "黑体";
font-family: "SimHei";
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before
content: "微软雅黑";
font-family: "Microsoft YaHei";
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before
content: "楷体";
font-family: "KaiTi";
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before
content: "仿宋";
font-family: "FangSong";
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before
content: "Arial";
font-family: "Arial";
/* .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before
content: "Times New Roman";
font-family: "Times New Roman";
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before
content: "sans-serif";
font-family: "sans-serif";
*/
.ql-font-SimSun
font-family: "SimSun";
.ql-font-SimHei
font-family: "SimHei";
.ql-font-Microsoft-YaHei
font-family: "Microsoft YaHei";
.ql-font-KaiTi
font-family: "KaiTi";
.ql-font-FangSong
font-family: "FangSong";
.ql-font-Arial
font-family: "Arial";
/* .ql-font-Times-New-Roman
font-family: "Times New Roman";
.ql-font-sans-serif
font-family: "sans-serif";
*/
2、自定义图片和视频上传(Element UI + OSS)
<div id="toolbar" slot="toolbar">
<button id="custom-button" @click.prevent="fnOpenUploadImage" title="图片"><i class="iconfont icon-tupian"></i></button>
<button id="custom-button" @click.prevent="fnOpenUploadVideo" title="视频"><i class="iconfont icon-video2"></i></button>
</div>
自定义图片和视频上传使用的是Element UI + OSS上传组件,参考上一篇Vue Element UI + OSS上传文件,上传成功之后需要把图片或者视频插入内容区域,通过查看Quill文档,
insertEmbed(index: Number, type: String, value: any, source: String = 'api'): Delta
可以插入视频和图片:
/**
* [fnUploadSucess 提交上传文件函数]
* @author zoumiao
* @param Array uploadFileUrlList [上传文件返回的url]
* @return null [没有返回]
*/
fnUploadSucess (uploadFileUrlList)
this.editor.focus()
for (let url of uploadFileUrlList)
this.editor.insertEmbed(this.editor.getSelection().index, this.uploadType, url)
3、字数统计
Quill没有字节提供字数统计,API提供了getLength方法,但是如果输入空格,该方法也会计算为字符;通过editor-change事件来计算字符个数:
/**
* @description [onEditorChange 输入文本改变事件]
* @author zoumiao
* @param Object editor 返回的编辑对象html, text, quill
* @return null [没有返回]
*/
onEditorChange (editor)
let html = editor.html
this.preContent = html
this.$emit('input', html)
this.contentLength = this._.trim(editor.text).length
,
4、图片可收缩
import ImageDrop from 'quill-image-drop-module'
import ImageResize from 'quill-image-resize-module'
Quill.register('modules/imageDrop', ImageDrop)
Quill.register('modules/imageResize', ImageResize)
Quill配置项中也要进行配置:
editorOption:
modules:
toolbar: '#toolbar',
history:
delay: 1000,
maxStack: 50,
userOnly: false
,
imageDrop: true,
imageResize:
displayStyles:
backgroundColor: 'black',
border: 'none',
color: 'white'
,
modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]
在开发环境中使用这两个插件没有什么问题,但是生成打包之后会报错,vue-quill-editor 的quill-image-resize-module 改变插入图片大小模块引入报错问题
ueditor自定义图片视频尺寸校验(代码片段)
...校验,对于文件宽高尺寸校验暂不支持。这里记录一下自定义图片、视频尺寸校验过程,内容核心主要是扩展校验逻辑和增加自定义提示文本。单图上传单图上传的逻辑在ueditor.all.js中,由simpleUpload组件实现,其内部是通过监听f... 查看详情
vuequill富文本自定义上传音频/视频(代码片段)
...Asp.NetCore文件上传服务(本文不提供/框架很多)Vue2.0功能自定义图片上传自定义视频上传自定义音频上传效果代码从若依框架中把Editor/index.vue复制出来,一顿改改改~也不知道看了多少人的笔记。Editor/index.vue<template><div>... 查看详情
vuequill富文本自定义上传音频/视频(代码片段)
...Asp.NetCore文件上传服务(本文不提供/框架很多)Vue2.0功能自定义图片上传自定义视频上传自定义音频上传效果代码从若依框架中把Editor/index.vue复制出来,一顿改改改~也不知道看了多少人的笔记。Editor/index.vue<template><div>... 查看详情
el-upload上传视频没有封面
...想要的封面图片,然后将实际的文件路径存放到另一个自定义字段里面就好,这里我的自定义字段为“realurl“ 参考技术B由于el-upload展示封面时根据file-list参数中的url字段来进行展示的,所以我们在处理数据的时候我们将其中的... 查看详情
vue结合element框架实现图片,文件,视频上传(代码片段)
...文件的属性修改(这几个是常用,特记录下):1.自定义上传(默认为自动上传)需要设置::auto-upload=“false”2.name=‘appFile’:默认的上传的参数名为file,可以用name属性来修改3.:data属性是设置自... 查看详情
vue结合element框架实现图片,文件,视频上传(代码片段)
...文件的属性修改(这几个是常用,特记录下):1.自定义上传(默认为自动上传)需要设置::auto-upload=“false”2.name=‘appFile’:默认的上传的参数名为file,可以用name属性来修改3.:data属性是设置自... 查看详情
ckeditor添加自定义按钮整合swfupload实现批量上传图片
ckeditor添加自定义按钮整合swfupload实现批量上传图片给ckeditor添加自定义按钮,由于ckeditor只能上传一张图片,如果要上传多张图片就要结合ckfinder,而ckfinder是收费的,所以就想通过自定义按钮整合swfupload实现一次上传多张图片... 查看详情
Wordpress 自定义元框多张图片上传
】Wordpress自定义元框多张图片上传【英文标题】:Wordpresscustommetaboxmultipleimageupload【发布时间】:2016-08-2523:08:07【问题描述】:我正在使用添加了自定义元框的自定义帖子类型。在自定义元框中,我正在尝试为多个图像添加媒体... 查看详情
ueditor样式过滤去除和远程图片上传自定义(代码片段)
ueditor自定义编辑的时候,比如需要做延迟加载,这个时候需要自定义图片等,但是,ueditor会去除img上面的属性,比如data-original和把远程图片自动上传。这个时候,首先,需要给图片自动上传加上属性,不如对于jquery.lazyload延迟... 查看详情
如何使用自定义 UITableView 上传图片
】如何使用自定义UITableView上传图片【英文标题】:HowToUploadImageUsingCustomUITableView【发布时间】:2020-06-1505:40:27【问题描述】:我对iOS开发比较陌生。我想知道如何允许用户单击表格中的一行并提示他们上传图像?我知道如何创... 查看详情
9行代码实现图片上传和预览(自定义按钮上传)(代码片段)
9行代码实现图片上传和预览(自定义按钮上传)结果展示:默认展示2.点击按钮后,选择图片文件图片预览首先我们定义一个type为file的input,并将它隐藏,绘制一个button,在按钮点击事件中触发input的点击事件。... 查看详情
9行代码实现图片上传和预览(自定义按钮上传)(代码片段)
9行代码实现图片上传和预览(自定义按钮上传)结果展示:默认展示2.点击按钮后,选择图片文件图片预览首先我们定义一个type为file的input,并将它隐藏,绘制一个button,在按钮点击事件中触发input的点击事件。... 查看详情
html自定义上传图片样式,并回显
<divid="photoUpLoad"><inputtype="file"id="photo"name=""accept="image/*"><spanclass="cross">+</span><imgsrc=""alt=""id="photoEcho"></div> #photoUpLoad{wid 查看详情
elementupload一次性上传多张图片(包含自定义上传不走action)
最重要的都圈出来了 查看详情
vue2.0自定义图片上传(uploader)组件
1.自定义组件UpLoader.vue<!--上传图片组件--><template><divclass="vue-uploader"><!--添加图片及显示效果--><divclass="file-list"><!--图片列表files--><sectionv-for="(file,index)offiles"class 查看详情
vue结合element实现自定义上传图片文件
参考了很多文献,感谢各位帖子,所以也想把自己遇到不会的东西分享出来,菜鸟一枚大家一进步! 查看详情
百度富文本编辑器ueditor自定义上传图片接口
如下图: 然后修改ueditor.all.js 查看详情
ueditor上传图片后自定义样式类名
...使用前端语言的话是不可能的,这就需要我们在开发时就定义好整个样式。正常情况下,我们上传后的图片是固定了宽高的,要想适用移动端,就必须使宽度定义为100%,或者加个类上去,我们可以自定义。这就是我们的思路。... 查看详情