效果图:
一、 安装vue-quill-editor
npm install vue-quill-editor -S
//若是报错 Cannot read property 'imports' of undefined,请安装
// 为了避免意外还是先安装一下quill
npm install quill --save
1.2.、全局引入在main.js 中写入
// 富文本编辑器
import VueQuillEditor from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
Vue.use(VueQuillEditor)
1.3、再页面中引入
<template>
<div>
<quill-editor ref="myQuillEditor" v-model="content" :options="editorOption"
class="my-quill-editor"></quill-editor>
</div>
</template>
<script>
export default {
data() {
return {
content: '',
editorOption: {
// 占位配置
placeholder: '请输入...',
height: '300px',
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' }], // 文本方向
['link', 'image', 'video'], // 链接、图片、视频
[{ align: [] }], // 添加居中按钮
[{ color: [] }], // 文字颜色按钮
]
},
// 更改插入的图片大小
imageResize: {
displayStyles: {
backgroundColor: "black",
border: "none",
color: "white",
},
modules: ["Resize", "DisplaySize", "Toolbar"],
},
}
},
}
}
}
</script>
<style>
/* 编辑器高度及背景色 */
::v-deep .ql-editor {
min-height: 300px;
background-color: #fff;
}
</style>
二、 更改插入图片大小、 安装
npm install quill
npm install quill-image-resize-module --save
npm install quill-image-drop-module --save
2.1、 main.js引入
// 富文本图片大小
import imageResize from 'quill-image-resize-module' // 调整大小组件。
import { ImageDrop } from 'quill-image-drop-module'; // 拖动加载图片组件。
Quill.register('modules/imageResize', imageResize );
Quill.register('modules/imageDrop', ImageDrop);
2.2、 在vue.config.js文件中引入
//下载 webpack
npm install webpack -s
// vue.config.js :
const webpack = require('webpack');
module.exports = {
configureWebpack: {
// 在这里添加你的自定义Webpack配置
plugins: [
new webpack.ProvidePlugin({
'window.Quill': 'quill/dist/quill.js',
'Quill': 'quill/dist/quill.js'
})
]
}
};
配置完成后记得重启一下
2.3、 在页面中显示样式
<template>
<div>
// 添加 class="ql-editor" 样式,不然文字居中不会显示
<div v-html="content" class="ql-editor"></div>
</div>
</template>
2.4、 图片适配PC后,手机端显示太大,如果只有一处调用就在页面中添加css样式,如何多次调用在app.vue中添加css样式
/* PC端样式 */
@media (min-width: 768px) {
.ql-editor img {
max-width: 100%;
height: auto;
}
}
/* 手机端样式 */
@media (max-width: 767px) {
.ql-editor img {
width: 100%;
height: auto;
}
}
2.5、 如果富文本编辑器内容过多,会将富文本编辑器功能按钮移除屏幕
解决方法,固定高度,使用默认滚动条
.ql-toolbar.ql-snow+.ql-container.ql-snow {
height: 600px;
}
三、自定义视频上传
3.1、添加组件,引入修改的video模块并注册、替换默认上传
<template>
<div>
<quill-editor ref="myQuillEditor" v-model="content" :options="editorOption"
class="my-quill-editor"></quill-editor>
// 上传组件
<el-dialog title="视频" :visible.sync="dialogFormVisible" style="text-align: center;">
<el-upload class="upload-demo" drag action="上传地址" multiple
:before-upload="onBeforeUploadVideo" :on-success="onSuccessVideo"
:on-error="onErrorVideo">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传MP4文件,且不超过10M</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
// 这里引入修改的video模块并注册,utils/quillVideo在下方复制
import Video from "@/utils/quillVideo";
Quill.register(Video, true);
export default {
data() {
return {
content: '',
dialogFormVisible: false,
editorOption: {
// 占位配置
placeholder: '请输入...',
height: '300px',
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' }], // 文本方向
['link', 'image', 'video'], // 链接、图片、视频
[{ align: [] }], // 添加居中按钮
[{ color: [] }], // 文字颜色按钮
],
handlers: {
// 点击上传视频阻止默认上传替换成自定义上传
video: () => {
this.dialogFormVisible = true;
},
},
},
// 更改插入的图片大小
imageResize: {
displayStyles: {
backgroundColor: "black",
border: "none",
color: "white",
},
modules: ["Resize", "DisplaySize", "Toolbar"],
},
}
},
}
}
}
</script>
<style>
/* 编辑器高度及背景色 */
::v-deep .ql-editor {
min-height: 300px;
background-color: #fff;
}
</style>
// 这是 utils/quillVideo
import { Quill } from "vue-quill-editor";
// 源码中是import直接倒入,这里要用Quill.import引入
const BlockEmbed = Quill.import("blots/block/embed");
const Link = Quill.import("formats/link");
const ATTRIBUTES = ["height", "width"];
class Video extends BlockEmbed {
static create(value) {
const node = super.create();
// 添加video标签所需的属性
node.setAttribute('style', 'object-fit:fill;width: 30%;')
node.setAttribute('preload', 'auto') // auto - 当页面加载后载入整个视频 meta - 当页面加载后只载入元数据 none - 当页面加载后不载入视频
// node.setAttribute('playsinline', 'true')
// node.setAttribute('x-webkit-airplay', 'allow')
// node.setAttribute('x5-video-player-type', 'h5') // 启用H5播放器,是wechat安卓版特性
// node.setAttribute('x5-video-orientation', 'portraint') // 竖屏播放 声明了h5才能使用 播放器支付的方向,landscape横屏,portraint竖屏,默认值为竖屏
// node.setAttribute('x5-playsinline', 'true') // 兼容安卓 不全屏播放
node.setAttribute('x5-video-player-fullscreen', 'true') // 全屏设置,设置为 true 是防止横屏
node.setAttribute('controlsList', 'nofullscreen') // 控制删除
node.setAttribute("poster", value.poster); // 视频封面
node.setAttribute("controls", "controls");
node.setAttribute("type", "video/mp4");
node.setAttribute("src", this.sanitize(value.url));
return node;
}
static formats(domNode) {
return ATTRIBUTES.reduce((formats, attribute) => {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
static sanitize(url) {
return Link.sanitize(url);
}
static value(domNode) {
return {
url: domNode.getAttribute("src"),
poster: domNode.getAttribute('poster'),
}
}
format(name, value) {
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
html() {
const { video } = this.value();
return `<a href="${video}">${video}</a>`;
}
}
Video.blotName = "video"; // 这里不用改,楼主不用iframe,直接替换掉原来,如果需要也可以保留原来的,这里用个新的blot
Video.className = "ql-video";
Video.tagName = "video"; // 用video标签替换iframe
export default Video;
3.2、组件中的poster是视频封面,若不上传手机端只显示开始按钮,其余部分为空白()
quill.insertEmbed(index, "video", { url: videoLink, poster: 'https://7up.pics/images/2024/01/23/101sh.png' });
methods: {
// el-文件上传组件
onBeforeUploadVideo(file) {
let acceptArr = ["video/mp4"];
const isVideo = acceptArr.includes(file.type);
const isLt1M = file.size / 1024 / 1024 < 30;
if (!isVideo) {
this.$message.error("只能上传mp4格式文件!");
}
if (!isLt1M) {
this.$message.error(`上传文件大小不能超过 30MB!`);
}
return isLt1M && isVideo;
},
// 文件上传成功时的钩子
onSuccessVideo(res) {
if (res.code === 2000) {
this.insertVideoLink(BASE_URL + res.data);
} else {
this.$message.error(res.message);
}
},
// 文件上传失败时的钩子
onErrorVideo() {
this.$message.error("上传失败");
},
// 插入视频
insertVideoLink(videoLink) {
if (!videoLink) return this.$message.error("视频地址不能为空!");
this.dialogFormVisible = false;
let quill = this.$refs["myQuillEditor"].quill;
// 获取富文本
let range = quill.getSelection();
// 获取光标位置:当编辑器中没有输入文本时,这里获取到的 range 为 null
let index = range ? range.index : 0;
// 在光标所在位置 插入视频
quill.insertEmbed(index, "video", { url: videoLink, poster: 'https://7up.pics/images/2024/01/23/101sh.png' });
// 调整光标到最后
quill.setSelection(index + 1);
},
}