富文本编辑器中实现功能:基本文本格式、上传图片、上传视频、链接等。
- 安装富文本编辑器
cnpm install --save vue-quill-editor
- 引入富文本编辑器
import QuillEditor from 'vue-quill-editor'
Vue.use(QuillEditor)
注:可以放到main.js中全局引用,也可以放到对应页面中。
- 使用
自定义一个组件,代码中有详细注释。完整代码:
<template>
<el-card v-loading="quillUpdateImg">
<div slot="header" class="clearfix">
<el-button style="float: left; padding: 3px 0" class="el-icon-arrow-left" type="text" @click="$router.go(-1)">返回</el-button>
<span>{{moduleTitle}}</span>
<el-button style="float: right; padding: 3px 0" class="el-icon-check" type="text" @click="submit">提交</el-button>
</div>
<el-form ref="editor" :model="editor" :rules="rules" label-width="100px" label-position="right">
<el-form-item class="form-item-title" prop="editor_title" label="文章标题:">
<el-input v-model="editor.editor_title" placeholder="文章标题" @change="handleEditorChange"/>
</el-form-item>
<el-form-item class="form-item-content" prop="editor_content" label="文章内容:">
<quill-editor class="editor" v-model="editor.editor_content" ref="myQuillEditor" :options="editorOption"
@change="handleEditorChange">
</quill-editor>
</el-form-item>
</el-form>
<el-upload
class="avatar-uploader"
action
name="pic"
:show-file-list="false"
:http-request="handleUpload">
</el-upload>
</el-card>
</template>
<script>
// 工具栏配置
const toolbarOptions = [
['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: [] }], // 字体种类
[{ align: [] }], // 对齐方式
['clean'], // 清除文本格式
// ['link', 'image', 'video'] // 链接、图片、视频
['link', 'image'] // 链接、图片
]
export default {
props: {
loading: false,
moduleTitle: { // 编辑器标题
type: String,
default: '富文本编辑器'
},
uploadApi: { // 文件服务器上传接口
type: String,
default: ''
},
downloadApi: { // 文件服务器下载接口
type: String,
default: ''
},
title: { // 文本标题
type: String,
default: ''
},
content: { // 文本内容
type: String,
default: ''
},
maxSize: { // 文本大小
type: Number,
default: 4000 // 单位:kb
}
},
data () {
return {
quillUpdateImg: this.loading, // 根据图片上传状态来确定是否显示loading动画
editor: { // 编辑器中数据
editor_title: this.title,
editor_content: this.content
},
rules: { // 表单校验规则
editor_title: [{required: true, message: '请输入文章标题'}, {max: 100, message: '长度不超过100个字符'}],
editor_content: [{required: true, message: '请输入文章内容'}, {max: 10000, message: '长度不超过10000个字符'}]
},
editorOption: { // 编辑框操作事件
theme: 'snow', // or 'bubble'
placeholder: '您想说点什么?',
modules: {
toolbar: {
container: toolbarOptions,
// container: '#toolbar',
handlers: {
image: function (value) { // 上传图片
if (value) {
document.querySelector('.avatar-uploader input').click() // 触发input框选择图片文件
} else {
this.quill.format('image', false)
}
},
link: function (value) { // 添加链接
if (value) {
var href = prompt('请输入url')
this.quill.format('link', href)
} else {
this.quill.format('link', false)
}
}
}
}
}
}
}
},
methods: {
handleEditorChange () { // 编辑器中内容改变时,触发的事件。冒泡到父组件,参数为编辑器中内容
this.$emit('editor-change', this.editor)
},
handleUpload (param) { // 上传图片
if (!this.uploadApi || this.uploadApi === '') {
this.$message.error('无上传文件地址')
}
let formData = new FormData()
formData.append('files', param.file)
this.quillUpdateImg = true
this.$axios.post(
this.uploadApi, formData
).then(({data}) => {
if (data.statusCode === 233) {
let quill = this.$refs.myQuillEditor.quill // 获取富文本组件实例
let length = quill.getSelection().index // 获取光标所在位置
quill.insertEmbed(length, 'image', this.downloadApi + data.data.name) // 插入图片 res.url为服务器返回的图片地址
quill.setSelection(length + 1) // 调整光标到最后
} else {
this.$message.error('上传失败')
}
this.quillUpdateImg = false
})
},
submit () { // 提交,冒泡到父组件处理
this.$refs.editor.validate(valid => {
if (valid) {
this.$emit('do-submit')
} else {
return false
}
})
}
}
}
</script>
<style lang="scss" scoped>
.el-card {
height: 100%;
/deep/ .el-card__header {
padding: 10px;
}
/deep/ .el-card__body {
padding: 10px;
overflow: auto;
height: calc(100% - 60px);
.el-form {
height: 100%;
.el-form-item {
margin-bottom: 20px;
}
.form-item-title {
height: 40px;
line-height: 40px;
}
.form-item-content {
height: calc(100% - 60px - 20px);
.el-form-item__content {
height: 100%;
}
.editor {
height: 100%;
line-height: normal !important;
.ql-toolbar {
height: 70px;
padding: 10px;
text-align: left;
}
.ql-container {
overflow: auto;
height: calc(100% - 70px);
}
}
}
}
.avatar-uploader {
height: 0;
}
.ql-snow .ql-tooltip[data-mode=link]::before {
content: '请输入链接地址:';
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: '保存';
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode=video]::before {
content: '请输入视频地址:';
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: '14px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
content: '10px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
content: '18px';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
content: '32px';
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: '文本';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
content: '标题1';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
content: '标题2';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
content: '标题3';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
content: '标题4';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
content: '标题5';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
content: '标题6';
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: '标准字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
content: '衬线字体';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
content: '等宽字体';
}
}
}
</style>
注:编辑器中无法实现行距的设置,待进一步完善。