vue-Quill富文本修改上传图片或粘贴图片到服务器方式以及汉化提示
vue-Quill修改上传图片或粘贴图片为上传到服务器方式以及汉化
·
引用Quill组件
1.下载组件。在package中引入:
"@vueup/vue-quill": "^1.0.0-beta.8",
"vue-quill-editor": "^3.0.6",
"quill-image-resize-module": "^3.0.0",
"quill-image-drop-module": "^1.0.3",
"quill-image-paste-module": "^1.0.6",
2.在vue.config.js中添加以下代码,不然会报错。
chainWebpack: config => {
config.plugin('provide').use(webpack.ProvidePlugin, [{
'window.Quill': 'quill/dist/quill.js',
'Quill': 'quill/dist/quill.js'
}])
}
3.引用组件
import { QuillEditor, Quill } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
import { UPLOAD_IMAGE_PATH } from '@/api/BASE_MODULE/upload'//上传图片接口
import $ from 'jquery'
import ImageResize from 'quill-image-resize-module' // 调整大小组件。
Quill.register('modules/ImageResize', ImageResize)
import { ImageDrop } from 'quill-image-drop-module'
Quill.register('modules/imageDrop', ImageDrop)
import { ImageExtend } from 'quill-image-paste-module'
Quill.register('modules/ImageExtend', ImageExtend)
自定义富文本
引用位置
<div class="editor-wrap" id="editor-wrap">
<QuillEditor ref="editorWrap" :options="options" theme="snow" @update:content="onEditorChange" />
</div>
添加工具栏
此处并不包含上传图片和汉化功能。使用时需要在此处引用:
components: {
QuillEditor
}
引用工具栏:
data() {
return {
options: {
modules: {
toolbar: {
container: [
['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'], // 清除文本格式
['image'] // 链接、图片、视频
],
}
}
},
}
},
// 内容改变事件
onEditorChange(params) {
const contentText = this.$refs.editorWrap.getText()
if (contentText === '') {
this.temp.typeDescribe = ''
} else {
//console.log(this.$refs.editorWrap.getHTML())
//const str = this.$refs.editorWrap.getHTML()
this.temp.typeDescribe = this.$refs.editorWrap.getHTML()
//this.temp.content = str.replace(/"/g, '\"')
// console.log(this.$refs.editorWrap.getHTML())
}
},
添加汉化功能,鼠标放在图标上会出现提示
1.把以下代码添加到options外,return内:
tooltips: [
{ choice: 'ql-bold', title: '加粗' },
{ choice: 'ql-italic', title: '斜体' },
{ choice: 'ql-size', title: '字体大小' },
{ choice: 'ql-underline', title: '下划线' },
{ choice: 'ql-strike', title: '删除线' },
{ choice: 'ql-link', title: '添加链接' },
{ choice: 'ql-image', title: '添加图片' },
{ choice: 'ql-color', title: '字体颜色' },
{ choice: 'ql-background', title: '背景颜色' },
{ choice: 'ql-clean', title: '清除格式' },
{ choice: 'super', title: '上标' },
{ choice: 'sub', title: '下标' },
{ choice: 'ordered', title: '编号列表' },
{ choice: 'bullet', title: '项目列表' },
{ choice: '-1', title: '向左缩进' },
{ choice: '+1', title: '向右缩进' }
],
2.把以下代码放在mounted中:
const buttonList = $('.ql-toolbar').find('button[type="button"]')
const _this = this
buttonList.each(function(i) {
const currentClass = $(this).attr('class')
const currentValue = $(this).attr('value')
for (const item of _this.tooltips) {
if (item.choice === currentClass) {
$(this).prop('title', item.title)
} else if (item.choice === currentValue) {
$(this).prop('title', item.title)
} else {
continue
}
}
})
$('span.ql-align .ql-picker-label').each(function(i) {
$(this).prop('title', '对齐方式')
})
$('span.ql-align .ql-picker-options .ql-picker-item').each(function(i) {
switch (i) {
case 0:
$(this).prop('title', '左对齐')
break
case 1:
$(this).prop('title', '居中对齐')
break
case 2:
$(this).prop('title', '右对齐')
break
case 3:
$(this).prop('title', '两端对齐')
break
}
})
添加上传图片,支持粘贴
由于富文本在上传图片时,默认存储的为base64格式,这样会占用很大的内存空间,通过劫持上传按钮,我们可以自定义上传功能,把地址存储到数据库,并插入image标签,通过图片地址回显。
1、添加上传按钮,并加以隐藏
<!-- 上传图片组件 隐藏 -->
<el-upload
class="avatar-uploader"
:action="upload.url"
name='upfile'
:headers="headerObj"//可选
:show-file-list="false"
:on-success="uploadSuccess"
:on-error="uploadError"
:before-upload="beforeUpload">
</el-upload>
2、在modules中加入以下代码:
// 新增下面
imageDrop: false, // 拖动加载图片组件。
imageResize: { //调整大小组件。
displayStyles: {
backgroundColor: 'black',
border: 'none',
color: 'white'
},
modules: ['Resize', 'DisplaySize', 'Toolbar']
},
clipboard: {
// 粘贴版,处理粘贴时候带图片
matchers: [[Node.ELEMENT_NODE, this.handleCustomMatcher]]
},
劫持点击上传图标事件:
handlers: {
'image': function(value) {
if (value) {
document.querySelector('.avatar-uploader input').click()
} else {
this.Quill.format('image', false)
}
}
}
3、在mounted中添加以下代码:
// 自定义粘贴图片功能
const quill = this.$refs.editorWrap.getQuill()
quill.root.addEventListener(
'paste',
evt => {
if (
evt.clipboardData &&
evt.clipboardData.files &&
evt.clipboardData.files.length
) {
evt.preventDefault();
[].forEach.call(evt.clipboardData.files, file => {
if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
return false
}
const formData = new FormData()
formData.append('upfile', file) //后台上传接口的参数名
// 实现上传
$.ajax({
type: 'post',
url: UPLOAD_IMAGE_PATH, // 上传的图片服务器地址
data: formData,
dataType: 'json',
processData: false,
contentType: false, //设置文件上传的type值,必须
success: (response) => {
if (response.code === 2000) {
console.log(response)
// 获取光标所在位置
const length = quill.getSelection().index
// 插入图片 dt.url为服务器返回的图片地址
quill.insertEmbed(length, 'image', process.env.VUE_APP_API_URL + response.data.filePath)
// 调整光标到最后
quill.setSelection(length + 1)
}
},
error: function() {
this.$message.error('上传失败!')
}
})
})
}
}
)
4、在methods中添加以下代码:
// 上传前校检格式和大小
beforeUpload(file) {
const regArr = ['.gif', '.jpg', '.jpeg', '.png']
const lastName = file.name.slice(file.name.lastIndexOf('.'))
if (regArr.indexOf(lastName) === -1) {
this.$message.error(`仅支持.gif/.jpg/.jpeg/.png格式!`)
return false
}
// 校检文件大小
const isLt = file.size / 1024 / 1024 < 5
if (!isLt) {
this.$message.error(`上传文件大小不能超过5MB!`)
return false
}
return true
},
//上传成功或者失败
uploadSuccess(res) {
// console.log(res)
// console.log(res.data.filePath)
const quill = this.$refs.editorWrap.getQuill()
// 如果上传成功
if (res.code === 2000 && res.filePath !== null) {
// 获取光标所在位置
const length = quill.getSelection().index
// 插入图片 dt.url为服务器返回的图片地址
quill.insertEmbed(length, 'image', process.env.VUE_APP_API_URL + res.data.filePath)
// 调整光标到最后
quill.setSelection(length + 1)
} else {
this.$message.error('图片插入失败')
}
// loading加载隐藏
this.quillUpdateImg = false
},
//上传失败
uploadError() {
this.$message.error('图片插入失败')
},
handleCustomMatcher(node, Delta) {
// 文字,从别处复制而来,清除自带样式,转为纯文本
if (node.src && node.src.indexOf('data:image/png') > -1) {
Delta.ops = []
return Delta
}
const ops = []
Delta.ops.forEach(op => {
if (op.insert && typeof op.insert === 'string') {
ops.push({
insert: op.insert
})
} else if (op.insert && typeof op.insert.image === 'string') {
ops.push({
insert: op.insert
})
}
})
Delta.ops = ops
return Delta
},
// 内容改变事件
onEditorChange(params) {
const contentText = this.$refs.editorWrap.getText()
if (contentText === '') {
this.temp.typeDescribe = ''
} else {
//console.log(this.$refs.editorWrap.getHTML())
//const str = this.$refs.editorWrap.getHTML()
this.temp.typeDescribe = this.$refs.editorWrap.getHTML()
//this.temp.content = str.replace(/"/g, '\"')
// console.log(this.$refs.editorWrap.getHTML())
}
},
完整示例:
<template>
<div class="">
<el-form
:rules="rules"
ref="dataForm"
:model="temp"
label-width="100px"
style='width:100%;'
>
<el-form-item label="上级分类:">
{{temp.parentName}}
</el-form-item>
<el-form-item label="分类名称:" prop="softwareTypeName">
<el-input v-model="temp.softwareTypeName" placeholder="请输入软件分类名称"></el-input>
</el-form-item>
<el-form-item label="分类说明:" prop="typeDescribe">
<!-- <textarea v-model="temp.typeDescribe" placeholder="请输入分类说明" style="height:80px;width:100%;"></textarea> -->
<div class="editor-wrap" id="editor-wrap">
<QuillEditor ref="editorWrap" :options="options" theme="snow" @update:content="onEditorChange" />
</div>
<!-- 上传图片组件隐藏 -->
<el-upload
class="avatar-uploader"
:action="upload.url"
name='upfile'
:headers="headerObj"//可选
:show-file-list="false"
:on-success="uploadSuccess"
:on-error="uploadError"
:before-upload="beforeUpload">
</el-upload>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { QuillEditor, Quill } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
import { mapGetters } from 'vuex'
// import { toRaw } from 'vue'
import { UPLOAD_IMAGE_PATH } from '@/api/BASE_MODULE/upload'
import $ from 'jquery'
import ImageResize from 'quill-image-resize-module' // 调整大小组件。
Quill.register('modules/ImageResize', ImageResize)
import { ImageDrop } from 'quill-image-drop-module'
Quill.register('modules/imageDrop', ImageDrop)
import { ImageExtend } from 'quill-image-paste-module'
Quill.register('modules/ImageExtend', ImageExtend)
export default {
name: 'Create',
computed: {
...mapGetters([
'drawerCurrent'
]),
drawerType: function() {
return this.drawerCurrent.drawerType
}
},
components: {
QuillEditor
},
data() {
return {
// 用户导入参数
upload: {
// 设置上传的请求头部
// headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: UPLOAD_IMAGE_PATH
},
options: {
modules: {
// 新增下面
imageDrop: false, // 拖动加载图片组件。
imageResize: { //调整大小组件。
displayStyles: {
backgroundColor: 'black',
border: 'none',
color: 'white'
},
modules: ['Resize', 'DisplaySize', 'Toolbar']
},
clipboard: {
// 粘贴版,处理粘贴时候带图片
matchers: [[Node.ELEMENT_NODE, this.handleCustomMatcher]]
},
toolbar: {
container: [
['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'], // 清除文本格式
['image'] // 链接、图片、视频
],
handlers: {
'image': function(value) {
if (value) {
document.querySelector('.avatar-uploader input').click()
} else {
this.Quill.format('image', false)
}
}
}
}
}
},
tooltips: [
{ choice: 'ql-bold', title: '加粗' },
{ choice: 'ql-italic', title: '斜体' },
{ choice: 'ql-size', title: '字体大小' },
{ choice: 'ql-underline', title: '下划线' },
{ choice: 'ql-strike', title: '删除线' },
{ choice: 'ql-link', title: '添加链接' },
{ choice: 'ql-image', title: '添加图片' },
{ choice: 'ql-color', title: '字体颜色' },
{ choice: 'ql-background', title: '背景颜色' },
{ choice: 'ql-clean', title: '清除格式' },
{ choice: 'super', title: '上标' },
{ choice: 'sub', title: '下标' },
{ choice: 'ordered', title: '编号列表' },
{ choice: 'bullet', title: '项目列表' },
{ choice: '-1', title: '向左缩进' },
{ choice: '+1', title: '向右缩进' }
],
temp: {
status: 1,
parentName: '',
parentId: ''
},
rules: {
softwareTypeName: [
{ required: true, message: '请输入软件类型名称', trigger: 'change' },
{ max: 50, message: '名称最大长度50位', trigger: 'change' }
],
describe: [
{ required: true, message: '请输入软件类型描述', trigger: 'change' }
]
}
}
},
watch: {
// 表单内容体
temp: {
handler: function(val, oldVal) {
},
deep: true
}
},
created() {
},
mounted() {
// 自定义粘贴图片功能
const quill = this.$refs.editorWrap.getQuill()
quill.root.addEventListener(
'paste',
evt => {
if (
evt.clipboardData &&
evt.clipboardData.files &&
evt.clipboardData.files.length
) {
evt.preventDefault();
[].forEach.call(evt.clipboardData.files, file => {
if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
return false
}
const formData = new FormData()
formData.append('upfile', file) //后台上传接口的参数名
// 实现上传
$.ajax({
type: 'post',
url: UPLOAD_IMAGE_PATH, // 上传的图片服务器地址
data: formData,
dataType: 'json',
processData: false,
contentType: false, //设置文件上传的type值,必须
success: (response) => {
if (response.code === 2000) {
console.log(response)
// 获取光标所在位置
const length = quill.getSelection().index
// 插入图片 dt.url为服务器返回的图片地址
quill.insertEmbed(length, 'image', process.env.VUE_APP_API_URL + response.data.filePath)
// 调整光标到最后
quill.setSelection(length + 1)
}
},
error: function() {
this.$message.error('上传失败!')
}
})
})
}
}
)
const temp = this.drawerCurrent.params
// console.log(temp)
if (this.drawerType === 'CREATE_SECTION') {
this.temp = Object.assign({}, this.temp, temp)
} else {
this.temp.parentId = temp.parentId
this.temp.parentName = temp.parentName
this.temp.softwareTypeName = temp.softwareTypeName
this.temp.typeDescribe = temp.typeDescribe
this.temp.id = temp.id
this.$refs.editorWrap.setHTML(temp.typeDescribe)
}
const buttonList = $('.ql-toolbar').find('button[type="button"]')
const _this = this
buttonList.each(function(i) {
const currentClass = $(this).attr('class')
const currentValue = $(this).attr('value')
for (const item of _this.tooltips) {
if (item.choice === currentClass) {
$(this).prop('title', item.title)
} else if (item.choice === currentValue) {
$(this).prop('title', item.title)
} else {
continue
}
}
})
$('span.ql-align .ql-picker-label').each(function(i) {
$(this).prop('title', '对齐方式')
})
$('span.ql-align .ql-picker-options .ql-picker-item').each(function(i) {
switch (i) {
case 0:
$(this).prop('title', '左对齐')
break
case 1:
$(this).prop('title', '居中对齐')
break
case 2:
$(this).prop('title', '右对齐')
break
case 3:
$(this).prop('title', '两端对齐')
break
}
})
},
methods: {
// 上传前校检格式和大小
beforeUpload(file) {
const regArr = ['.gif', '.jpg', '.jpeg', '.png']
const lastName = file.name.slice(file.name.lastIndexOf('.'))
if (regArr.indexOf(lastName) === -1) {
this.$message.error(`仅支持.gif/.jpg/.jpeg/.png格式!`)
return false
}
// 校检文件大小
const isLt = file.size / 1024 / 1024 < 5
if (!isLt) {
this.$message.error(`上传文件大小不能超过5MB!`)
return false
}
return true
},
//上传成功或者失败
uploadSuccess(res) {
// console.log(res)
// console.log(res.data.filePath)
const quill = this.$refs.editorWrap.getQuill()
// 如果上传成功
if (res.code === 2000 && res.filePath !== null) {
// 获取光标所在位置
const length = quill.getSelection().index
// 插入图片 dt.url为服务器返回的图片地址
quill.insertEmbed(length, 'image', process.env.VUE_APP_API_URL + res.data.filePath)
// 调整光标到最后
quill.setSelection(length + 1)
} else {
this.$message.error('图片插入失败')
}
// loading加载隐藏
this.quillUpdateImg = false
},
//上传失败
uploadError() {
this.$message.error('图片插入失败')
},
handleCustomMatcher(node, Delta) {
// 文字,从别处复制而来,清除自带样式,转为纯文本
if (node.src && node.src.indexOf('data:image/png') > -1) {
Delta.ops = []
return Delta
}
const ops = []
Delta.ops.forEach(op => {
if (op.insert && typeof op.insert === 'string') {
ops.push({
insert: op.insert
})
} else if (op.insert && typeof op.insert.image === 'string') {
ops.push({
insert: op.insert
})
}
})
Delta.ops = ops
return Delta
},
// 内容改变事件
onEditorChange(params) {
const contentText = this.$refs.editorWrap.getText()
if (contentText === '') {
this.temp.typeDescribe = ''
} else {
//console.log(this.$refs.editorWrap.getHTML())
//const str = this.$refs.editorWrap.getHTML()
this.temp.typeDescribe = this.$refs.editorWrap.getHTML()
//this.temp.content = str.replace(/"/g, '\"')
// console.log(this.$refs.editorWrap.getHTML())
}
},
// 提交表单
submitForm() {
return this.$refs['dataForm'].validate()
},
// 获取表单数据
getFormData() {
// console.log(this.temp)
return this.temp
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.editor-wrap {
display: block;
width: 100%;
height: 450px;
::v-deep .ql-container {
height: calc(100% - 80px);
}
}
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-label::before,
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: '文本';
}
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: '标题1' !important;
}
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: '标题2' !important;
}
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: '标题3' !important;
}
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: '标题4' !important;
}
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: '标题5' !important;
}
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
::v-deep .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: '标题6' !important;
}
</style>
更多推荐
已为社区贡献1条内容
所有评论(0)