vue-quill-editor 整合上传图片 图片大小 附件 图文混排 数据回显等问题
根据网上的各类教程总结了一下 再根据我自己项目的需求 进行添加和修改 实现的图如下首先安装需要的插件cnpm install vue-quill-editor -Scnpm install quill -Scnpm install quill-image-resize-module -Scnpm install quill-image-drop-module -S在project.config.j
·
根据网上的各类教程总结了一下 再根据我自己项目的需求 进行添加和修改 实现的图如下
首先安装需要的插件
cnpm install vue-quill-editor -S
cnpm install quill -S
cnpm install quill-image-resize-module -S
cnpm install quill-image-drop-module -S
在project.config.js下写入如下代码
const webpack = require("webpack")
const plugins = [
new webpack.ProvidePlugin({
'window.Quill': 'quill/dist/quill.js',
'Quill': 'quill/dist/quill.js'
})
];
然后就是创建一个quillEditor.vue来配置富文本框
创建一个index.vue 来使用这个富文本框
两个部分的源码都已经在下面了
下面是源码部分
所有重要的地方都已加注释
quillEditor.vue
<template>
<div class="editorBox">
<!-- 这里没有用v-model 是因为vue-quill-editor会把v-model中的html代码中的行内样式过滤-->
<!--而quill-image-resize-module 这个组件是给图片加行内样式的 -->
<!-- 所以不绑定 在使用组件的页面直接让el.editor.innerHTML = 数据库传来的content -->
<!-- 这样行内样式就不会被过滤了 在使用富文本的组件中有代码 -->
<quill-editor
class="editor"
ref="myQuillEditor"
:options="editorOption"
@blur="onEditorBlur($event)"
@focus="onEditorFocus($event)"
@change="onEditorChange($event)"
>
</quill-editor>
<!-- 这里是文件上传的窗口
action 为上传的地址
headers 为请求头 带token
-->
<el-upload
:action="action"
:headers="headers"
:show-file-list="false"
:accept="accept"
:on-success="uploadSuccess"
:on-error="uploadError"
:before-upload="beforeUpload"
class="avatar-uploader-editor"
/>
<!--
这里是上传附件的窗口
action 为上传附件的接口
headers为请求头
limit为 最大文件个数
fileList 为附件的list 通过父组件传值 来给filelist确定值
-->
<el-upload
class="upload-demo"
:action="attactAction"
:headers="headers"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
multiple
:limit="1"
:on-exceed="handleExceed"
:ref="upload"
:file-list="fileList">
<el-button
size="small"
type="primary"
@click="uploadBtn"
class="my-upload"
v-show="false"
style="height:0px">点击上传</el-button>
<div slot="tip" class="el-upload__tip" style="margin-top:0px">已选择附件:</div>
</el-upload>
</div>
</template>
<script>
import {deleteInfoFile ,getInfo} from "@/api/banner";
import { getToken } from "@/utils/auth";
// 在这里导入需要用到的插件
import { quillEditor } from "vue-quill-editor"; //调用编辑器
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
import "quill/dist/quill.bubble.css";
import Quill from 'quill'
import resizeImage from 'quill-image-resize-module' // 调整大小组件。
import { ImageDrop } from 'quill-image-drop-module'; // 拖动加载图片组件。
Quill.register('modules/imageDrop', ImageDrop);
Quill.register('modules/resizeImage ', resizeImage )
export default {
components: {
quillEditor,
},
props: {
//这个是富文本的内容
contentData: {
type: String,
default: "",
},
infoId: {
type: String,
default: ""
},
//这个是附件列表
file: {
type: Array
}
},
data() {
return {
fileList: [],
upload:"",
id:"",
content: "",
accept: "image/jpeg,image/jpg,image/png,image/vnd.microsoft.icon",
//headers 和 action 根据自己项目的来确定
headers: {
authorization: "authorization-text",
"x-auth-token": getToken(),
},
action: process.env.VUE_APP_BASE_API + "/xxxxxxxxxxxx",
attactAction: ``,
quillUpdateImg: false,
editorOption: {
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"], //上传图片、上传视频
['sourceEditor'] //附件
],
handlers: {
//这里对应自定义的功能栏
image: function (value) {
console.log(value, "value");
if (value) {
// 触发input框选择图片文件
document
.querySelector(".avatar-uploader-editor input")
.click();
} else {
this.quill.format("image", false);
}
},
sourceEditor: function () { // 添加工具方法
document.getElementsByClassName('my-upload')[0].click()
}
},
},
//下面是控制图片大小组件需要的内容
history: {
delay: 1000,
maxStack: 50,
userOnly: false
},
imageDrop: true,
imageResize: {
displayStyles: {
backgroundColor: 'black',
border: 'none',
color: 'white'
},
modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]
}
},
},
};
},
// computed:{
// infoId: function() {
// return this.id;
// }
// },
mounted() {
const titleConfig = {
"ql-bold": "加粗",
"ql-color": "颜色",
"ql-font": "字体",
"ql-code": "插入代码",
"ql-italic": "斜体",
"ql-link": "添加链接",
"ql-background": "背景颜色",
"ql-size": "字体大小",
"ql-strike": "删除线",
"ql-script": "上标/下标",
"ql-underline": "下划线",
"ql-blockquote": "引用",
"ql-header": "标题",
"ql-indent": "缩进",
"ql-list": "列表",
"ql-align": "文本对齐",
"ql-direction": "文本方向",
"ql-code-block": "代码块",
"ql-formula": "公式",
"ql-image": "图片",
"ql-video": "视频",
"ql-clean": "清除字体样式",
"ql-upload": "文件",
};
// 富文本提示信息
this.$nextTick(() => {
const oToolBar = document.getElementsByClassName("editor")[0];
console.log("toobar",oToolBar);
const aButton = oToolBar.querySelectorAll("button");
const aSelect = oToolBar.querySelectorAll("select");
aButton.forEach(function (item) {
if (item.className === "ql-script") {
item.value === "sub" ? (item.title = "下标") : (item.title = "上标");
//这里将功能栏中的按钮循环出来 根据不同的classname 来给定不同的提示信息
} else if(item.className === "ql-sourceEditor") {
item.title = "附件"
} else if (item.className === "ql-indent") {
item.value === "+1"
? (item.title = "向右缩进")
: (item.title = "向左缩进");
} else {
item.title = titleConfig[item.classList[0]];
}
});
aSelect.forEach(function (item) {
item.parentNode.title = titleConfig[item.classList[0]];
});
});
//这里给自定义的附件功能给定一个icon
this.myEl = this.$el.querySelector('.ql-sourceEditor');
this.$el.querySelector(
'.ql-sourceEditor'
).innerHTML = `<svg t="1592278063482" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6358" width="18" height="200"><path d="M704 341.333333h64a64 64 0 0 1 64 64v362.666667a64 64 0 0 1-64 64H256a64 64 0 0 1-64-64V405.333333a64 64 0 0 1 64-64h64v64h-64v362.666667h512V405.333333h-64v-64zM517.653333 124.629333l150.826667 150.826667-45.226667 45.269333-74.026666-74.005333v304.768h-64V247.616l-73.173334 73.130667-45.248-45.248 150.826667-150.848z" p-id="6359"></path></svg>`
},
watch: {
//监听父组件传值 令content 为父组件传来的值
contentData: {
handler(newVal) {
this.content = newVal;
this.$nextTick(() => {
//这里会设置富文本框的高度和宽度
this.$refs.myQuillEditor.quill.container.style.height = "400px";
this.$refs.myQuillEditor.quill.container.style.width = "945px";
})
},
deep: true,
immediate: true,
},
infoId: {
handler(newVal) {
console.log("这里是id",newVal)
this.id = newVal;
}
},
file: {
//这里监听附件列表
handler(newVal) {
this.fileList = newVal;
}
}
},
created() {
},
beforeUpdate() {
},
updated() {
},
methods: {
handleExceed (files, fileList) {
this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件`)
},
beforeRemove (file, fileList) {
return this.$confirm(`确定移除 ${ file.name }?`)
},
handleRemove (file, fileList) {
var deleteId = 0
if (file.id) {
for (var i = 0; i < this.fileList.length; i++) {
if (this.fileList[i].name === file.name) {
this.fileList.splice(i, 1)
deleteId = file.id
break
}
}
} else if (file.uid) {
for (var j = 0; j < this.enclosureIDs.length; j++) {
if (this.enclosureIDs[j].name === file.name) {
deleteId = this.enclosureIDs[j]['en_id']
this.enclosureIDs.splice(j, 1)
break
}
}
}
// for (var i = this.fileList.length - 1; i > 0; i--) {
// console.log(this.fileList[i])
// }
// this.$emit('contentData', this.quill.root.innerHTML, this.enclosureIDs)
// // 请求删除附件接口
deleteInfoFile(this.id, {
})
.then(res => {
// this.$emit('contentData', this.quill.root.innerHTML, this.enclosureIDs)
this.$message({
type: 'success',
message: '删除成功!'
})
})
},
handlePreview (file) {
},
uploadBtn () {
//在这里给定上传附件的action
this.attactAction = process.env.VUE_APP_BASE_API + `/xxxxx`,
console.log(this.attactAction)
console.log("这里是按钮id",this.id)
console.log("这里是按钮current",this.content)
},
uploadSuccess(response, file, fileList) {
let quill = this.$refs.myQuillEditor.quill;
// 如果上传成功
if (response.status == 200) {
// 获取光标所在位置
let length = quill.getSelection().index;
// 插入图片 response.body.path为服务器返回的图片地址
quill.insertEmbed(
length,
"image",
//这里服务器地址 存进去的照片的scr为下面的地址
"http://localhost:8084" + response.body.path
);
// 调整光标到最后
quill.setSelection(length + 1);
} else {
this.$message.error("图片上传失败,请重新上传!");
}
// loading动画消失
this.quillUpdateImg = false;
// this.$message.success('图片上传成功');
},
uploadError() {},
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.$message.error("上传图片大小不能超过 2MB!");
}
return isLt2M;
},
onEditorBlur(e) {},
onEditorFocus(e) {},
onEditorChange(e) {
//这里是富文本框内容发生变化时 就会给父组件传值 传的为html代码 在父组件接受
e.quill.container.style.height = "200px";
this.$emit("editorChange",e.html);
},
},
};
</script>
<style scoped>
::v-deep .editor.ql-blank {
min-height: 200px;
}
.editor {
padding: 0px;
width: 945px;
height: 400px;
margin: auto;
}
.avatar-uploader {
height: 0px;
}
</style>
<style>
.el-upload-list__item {
transition: none !important;
}
</style>
<style lang='scss' >
.editor {
line-height: normal !important;
height: 500px;
}
.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>
在这个组件中使用富文本
index.vue
<template>
<el-card type="admin-full" shadow="never">
<el-form ref="form" :model="formData" label-width="80px">
<el-form-item label="学院概况" prop="content">
<div style="text-align: center;"><span>请在调整图片对齐格式后再调整图片大小</span></div>
<!-- 这里面是我需要用到的内容 把富文本的内容传给contentData就可以了 -->
<!-- file为附件的列表 -->
<quillEditor :file="fileList" :infoId="formData.id" :contentData="formData.content" @editorChange="contentChange" />
</el-form-item>
</el-form>
<span style="float: right;margin-bottom: 10px;">
<el-button :loading="dialogBtnLoading" type="primary" @click="handleOk">
保存
</el-button>
</span>
</el-card>
</template>
<script>
import quillEditor from "@/pages/learn/views/admin/news/components/quillEditor.vue";
import UploaderFile from "@/components/Pages/UploaderFile/index";
import { validateLength } from "@/utils/validate";
import { getInfo,updateInfo,getInfoFile } from "@/api/banner";
import { getToken } from '@/utils/auth';
export default {
components: {
quillEditor,
UploaderFile
},
props: {
value: {
type: Boolean,
default: false
},
activeName: {
type: String,
default: "journalismInformation"
},
data: {
type: Object,
default: () => {}
},
type: {
type: String,
default: "create"
}
},
data() {
return {
uploadFileUrl: process.env.VUE_APP_BASE_API + "/files/public/work",
fileList: [],
httpHeaders: {
authorization: 'authorization-text',
'x-auth-token': getToken()
},
dialogBtnLoading: false,
formData: {},
};
},
computed: {
},
watch: {
},
created() {
this.initData();
},
methods: {
initData(){
//这里是我自己的接口 从数据库查出content
getInfo({
params: {
name: "xxxxx"
}
})
.then(res =>{
this.formData = res;
//===============这个地方是很重要的地方==========
//因为vue-quill-editor 会过滤行内样式 导致图片的居左居中等行内样式不会显示在富文本框
// 但是数据库中的content还是有行内样式的 只是被quill过滤 所以我们直接让他加载的时候
// 加载我们数据库中的内容 如果在富文本的地方双向绑定 就会被过滤
document.querySelector(".ql-editor").innerHTML = this.formData.content;
getInfoFile(res.id)
.then(res =>{
this.fileList.push(res);
})
})
},
contentChange(val) {
// 将content赋值为富文本组件传来的值
this.formData.content = val;
},
onPreview() {},
handleExceed(files, fileList) {
this.$message.warning("只可以上传十个附件");
},
onSuccess(response, fileList) {
console.log(response, "response");
let file = {
name: response.body.name,
id: response.body.id
};
this.fileList.push(file);
console.log(this.fileList, "fileList");
},
onRemove() {
},
onClose() {
this.$emit("input", false);
},
handleOk() {
this.formData.attachId=null;
this.$refs["form"].validate(valid => {
if (valid) {
this.dialogBtnLoading = true;
updateInfo({
data: { ...this.formData }
}).then(res => {
this.$emit("ok");
this.$message.success("保存成功");
this.onClose();
this.dialogBtnLoading = false;
});
this.$emit("ok");
}
});
},
handleSuccess(url) {
console.log("this.formData", this.formData);
this.formData.cover = url;
}
}
};
</script>
<style lang="scss">
@import "~@/styles/mixin.scss";
.set-container {
min-width: 157px;
.img-card {
img {
max-width: 100%;
vertical-align: top;
}
}
.img-add-card {
background: rgba(255, 255, 255, 1);
border-radius: 4px;
color: #666666;
line-height: 1.15;
@include fontSize(14px);
width: 157px;
height: 180px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
box-shadow: 0 0 4px 0 #e4e4e4;
border: 1px dashed #dcdfe6;
i {
font-size: 32px;
&:hover {
color: #4597fd;
}
}
}
}
</style>
更多推荐
已为社区贡献1条内容
所有评论(0)