vue-quill-editor 富文本编辑器(可上传视频图片),组件挂载的方式实现
1.安装
npm install vue-quill-editor --save
npm install quill-image-drop-module --save
npm install quill-image-resize-module --save
2.在组件下面新增组件 QlEditor
(1)index.vue
<template><div><div id='quillEditorQiniu'><!-- 基于elementUi的上传组件 el-upload begin--><el-uploadclass="avatar-uploader":action="uploadImgUrl":accept="'image/*,video/*'":show-file-list="false":on-success="uploadEditorSuccess":on-error="uploadEditorError":before-upload="beforeEditorUpload":headers="headers"></el-upload><!-- 基于elementUi的上传组件 el-upload end--><quill-editorclass="editor"v-model="content"ref="customQuillEditor":options="editorOption"@blur="onEditorBlur($event)"@focus="onEditorFocus($event)"@change="onEditorChange($event)"></quill-editor></div></div>
</template><script>
import Vue from 'vue'
//引入quill-editor编辑器
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)//实现quill-editor编辑器拖拽上传图片
import Quill from 'quill'
import { ImageDrop } from 'quill-image-drop-module'
Quill.register('modules/imageDrop', ImageDrop)//实现quill-editor编辑器调整图片尺寸
import ImageResize from 'quill-image-resize-module'
Quill.register('modules/imageResize', ImageResize)// 这里引入修改过的video模块并注册
import Video from './video'
Quill.register(Video, true)//获取登录token,引入文件,如果只是简单测试,没有上传文件是否登录的限制的话,
//这个token可以不用获取,文件可以不引入,把上面对应的上传文件携带请求头 :headers="headers" 这个代码删掉即可
import {getToken} from "@/utils/auth";const toolbarOptions = [['bold', 'italic', 'underline', 'strike'], // toggled buttons['blockquote', 'code-block'],[{'header': 1}, {'header': 2}], // custom button values[{'list': 'ordered'}, {'list': 'bullet'}],[{'script': 'sub'}, {'script': 'super'}], // superscript/subscript[{'indent': '-1'}, {'indent': '+1'}], // outdent/indent[{'direction': 'rtl'}], // text direction[{'size': ['small', false, 'large', 'huge']}], // custom dropdown[{'header': [1, 2, 3, 4, 5, 6, false]}],[{'color': []}, {'background': []}], // dropdown with defaults from theme[{'font': []}],[{'align': []}],['link', 'image', 'video'],['clean'] // remove formatting button
];
export default {name:"QlEditor",props: {/* 编辑器的内容 */value: {type: String,default: "",},/* 高度 */height: {type: Number,default: null,},/* 最小高度 */minHeight: {type: Number,default: null,},/* 只读 */readOnly: {type: Boolean,default: false,},/* 上传文件大小限制(MB) */fileSize: {type: Number,default: 5,},},model: {//v-model本质上是一个语法糖 这一步在父组件中可使用v-modelprop: "value",event: "change",},data(){return {headers: {Authorization: "Bearer " + getToken(),},uploadImgUrl:process.env.VUE_APP_BASE_API + "/common/upload",uploadUrlPath:"没有文件上传",quillUpdateImg:false,content:'', //最终保存的内容editorOption:{placeholder:'你想说什么?',modules: {imageResize: {displayStyles: {backgroundColor: 'black',border: 'none',color: 'white'},modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]},toolbar: {container: toolbarOptions, // 工具栏handlers: {'image': function (value) {if (value) {document.querySelector('#quillEditorQiniu .avatar-uploader input').click()} else {this.quill.format('image', false);}},'video': function (value) {if (value) {document.querySelector('#quillEditorQiniu .avatar-uploader input').click()} else {this.quill.format('video', false);}},}}}},}},methods:{//上传图片之前asyncbeforeEditorUpload(res, file){//显示上传动画this.quillUpdateImg = true;// const res1 = await uploadImage()// console.log(res1,'=====');// this.$emit('before',res, file)console.log(res);console.log(file);},// 上传图片成功uploadEditorSuccess(res, file) {//拼接出上传的图片在服务器的完整地址let url=res.url;let type=url.substring(url.lastIndexOf(".")+1);// 获取富文本组件实例let quill = this.$refs.customQuillEditor.quill;// 获取光标所在位置let length = quill.getSelection().index;// 插入图片||视频 res.info为服务器返回的图片地址if(type=='mp4' || type=='MP4'){window.jsValue=url;quill.insertEmbed(length, 'video', url)}else{quill.insertEmbed(length, 'image', url)}// 调整光标到最后quill.setSelection(length + 1)//取消上传动画this.quillUpdateImg = false;},// 上传(文件)图片失败uploadEditorError(res, file) {//页面提示this.$message.error('上传图片失败')//取消上传动画this.quillUpdateImg = false;},//上传组件返回的结果uploadResult:function (res){this.uploadUrlPath=res;},// 失去焦点事件onEditorBlur(quill) {console.log('editor blur!', quill)},// 获得焦点事件onEditorFocus(quill) {console.log('editor focus!', quill)},// 准备富文本编辑器onEditorReady(quill) {console.log('editor ready!', quill)},// 内容改变事件onEditorChange({ quill, html, text }) {console.log('editor change!', quill, html, text)this.content = html}},created () {},//只执行一次,加载执行mounted () {console.log("开始加载")// 初始给编辑器设置title},watch: {value: {handler(val) {if (val !== this.content) {this.content = val === null ? "" : val;}},immediate: true,},},
}
</script><style>
.editor {line-height: normal !important;height: 400px;margin-bottom: 50px;
}
.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>
(2)video.js
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(value)// console.log("js文件"+ window.jsValue)// 添加video标签所需的属性node.setAttribute('controls', 'controls') // 控制播放器//删除原生video的控制条的下载或者全屏按钮的方法//<video controls controlsList='nofullscreen nodownload noremote footbar' ></video>//不用哪个在下面加上哪个node.setAttribute('controlsList', 'nofullscreen') // 控制删除node.setAttribute('type', 'video/mp4')node.setAttribute('style', 'object-fit:fill;width: 100%;')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('src', window.jsValue)return node}static formats (domNode) {return ATTRIBUTES.reduce((formats, attribute) => {if (domNode.hasAttribute(attribute)) {formats[attribute] = domNode.getAttribute(attribute)}return formats}, {})}// static sanitize (url) {//// // eslint-disable-line import/no-named-as-default-member// }static value (domNode) {return domNode.getAttribute('src')}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}" rel="external nofollow" >${video}</a>`}
}
Video.blotName = 'video' // 这里不用改,楼主不用iframe,直接替换掉原来,如果需要也可以保留原来的,这里用个新的blot
Video.className = 'ql-video'
Video.tagName = 'video' // 用video标签替换iframeexport default Video
3.main.js
import Vue from 'vue'
import QlEditor from "@/components/QlEditor"
// 全局组件挂载
Vue.component('QlEditor', QlEditor)
4.vue.config.js中加入
const webpack = require('webpack');
new webpack.ProvidePlugin({'window.Quill': 'quill/dist/quill.js','Quill': 'quill/dist/quill.js'
}),
加入位置如图
参考:https://www.cnblogs.com/wihimi/p/14246911.html
5.页面使用
<el-form-item label="内容"><QlEditor v-model="form.content" ref="qlEditorRef" :min-height="192"/>
</el-form-item><script>
/** 提交按钮 */
submitForm() {this.$refs["form"].validate(valid => {this.form.content=this.$refs.qlEditorRef.content;});
},
</script>
6.上传效果图
参考:https://www.jb51.net/article/264842.htm
相关文章:

vue-quill-editor 富文本编辑器(可上传视频图片),组件挂载的方式实现
1.安装 npm install vue-quill-editor --save npm install quill-image-drop-module --save npm install quill-image-resize-module --save2.在组件下面新增组件 QlEditor (1)index.vue <template><div><div idquillEditorQiniu><!-- 基于element…...
入门编程第一步,从记住这些单词开始
** 入门编程第一步,从记住这些单词开始 ** 2023-10-18 一、交互式环境与 print 输出 1、print : 打印/输出 2、coding : 编码 3、syntax : 语法 4、error : 错误 5、invalid : 无效 6、idenfifier : 名称/标识符 7、character : 字符 二、字符串的操作&#x…...

[C++]使用OpenCV去除面积较小的连通域
这是后期补充的部分,和前期的代码不太一样 效果图 源代码 //测试 void CCutImageVS2013Dlg::OnBnClickedTestButton1() {vector<vector<Point> > contours; //轮廓数组vector<Point2d> centers; //轮廓质心坐标 vector<vector<Point&…...
vscode连接不上,终端ssh正常,一直输入密码正确但是无法登录
若是之前链结果突然等不上,使用第一个链接 若是第一次链接连不上,先使用第二个链接,在使用第一个链接 原因:原因是服务器端的wget命令不能使用,vscode需要服务器端下载个文件,无法下载就导致了如上的错误…...

Hive on Spark 配置
目录 1 Hive 引擎简介2 Hive on Spark 配置2.1 在 Hive 所在节点部署 Spark2.2 在hive中创建spark配置文件2.3 向 HDFS上传Spark纯净版 jar 包2.4 修改hive-site.xml文件2.5 Hive on Spark测试2.6 报错 1 Hive 引擎简介 Hive引擎包括:MR(默认)…...
ROS 基本
ROS创建自己的功能包 ROS中工作空间(workspace)是一个存放工程开发相关文件的文件夹,其中有四个文件夹。 src:代码空间(Source Space)build:编译空间(Build Space)devel:开发空间(Development Space)install:安装空间(Install Space) OK接下来创作工作空间&#…...

Pygame基础9-射击
简介 玩家用鼠标控制飞机(白色方块)移动,按下鼠标后,玩家所在位置出现子弹,子弹匀速向右飞行。 代码 没有什么新的东西,使用两个精灵类表示玩家和子弹。 有一个细节需要注意,当子弹飞出屏幕…...

Ps:颜色查找
颜色查找 Color Lookup命令通过应用预设的 LUT 来改变图像的色彩和调性,从而为摄影师和设计师提供了一种快速实现复杂色彩调整的方法,广泛应用于颜色分级、视觉风格的统一和创意色彩效果的制作。 Ps菜单:图像/调整/颜色查找 Adjustments/Colo…...

vue3+vite 模板vue3-element-admin框架如何关闭当前页面跳转 tabs
使用模版: 有来开源组织 / vue3-element-admin 需要关闭的.vue 页面增加以下方法 //setup 里import {LocationQuery, useRoute, useRouter} from "vue-router"; const router useRouter(); function close() {console.log(|--router.currentRoute.value, router.cur…...

JavaScript 对象管家 Proxy
JavaScript 在 ES6 中,引入了一个新的对象类型 Proxy,它可以用来代理另一个对象,并可以在代理过程中拦截、覆盖和定制对象的操作。Proxy 对象封装另一个对象并充当中间人,其提供了一个捕捉器函数,可以在代理对象上拦截…...

Qt + Vs联合开发
Qt + Vs联合开发 文章目录 Qt + Vs联合开发环境说明VS+Qt安装注意事项QtCreator msvc编译器配置Visual Studio 2019 + Qt 5.12.10Visual Studio 2015 + Qt5.12.10VsQt环境配置安装插件 Qt Visual Studio Tools插件配置Qt创建项目Vs创建Qt项目VsQt工程转换Vs工程转Qt工程Qt工程转…...

开源知识库平台Raneto--使用Docker部署Raneto
文章目录 一、Raneto介绍1.1 Raneto简介1.2 知识库介绍 二、阿里云环境2.1 环境规划2.2 部署介绍 三、环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Raneto镜像五、部署Raneto知识库平台5.1 创建挂载目录5.2 编辑config.js文件5.3 编…...

鸿蒙原OS开发实例:【ArkTS类库单次I/O任务开发】
Promise和async/await提供异步并发能力,适用于单次I/O任务的场景开发,本文以使用异步进行单次文件写入为例来提供指导。 实现单次I/O任务逻辑。 import fs from ohos.file.fs; import common from ohos.app.ability.common;async function write(data:…...

C语言:二叉树的构建
目录 一、二叉树的存储 1.1 顺序存储 1.2 链式存储 二、二叉树的顺序结构及实现 2.1堆的概念及结构 2.2堆的构建 2.3堆的插入 2.4堆顶的删除 2.5堆的完整代码 三、二叉树的链式结构及实现 3.1链式二叉树的构建 3.2链式二叉树的遍历 3.2.1前序遍历 …...
软件测试工程师面试汇总功能测试篇
Q:一、进行测试用例设计的时候用到的方法有哪些? A:最常使用的测试用例设计方法包括等价类划分法、边界值分析方法、场景法、错误推测法。其中,最容易 发现错误的是边界值法,使用最多的是场景法。以注册为例:首先从需求确定用户名…...
javaAPI1
API application pragramming interface 应用程序编程接口 除java.lang包以外,其他包中的类在使用时需要导入 建包 package com.abc.javabean; 导包格式,import 包名.类名 API使用技巧 1,先看关键字 2,看参数列表 3,看返回值类型 String 封装字符串和处理字符串的类…...

案例研究|DataEase实现物业数据可视化管理与决策支持
河北隆泰物业服务有限责任公司(以下简称为“隆泰物业”)创建于2002年,总部设在河北省高碑店市,具有国家一级物业管理企业资质,通过了质量体系、环境管理体系、职业健康安全管理体系等认证。自2016年至今,隆…...

Android Studio Iguana | 2023.2.1 补丁 1
Android Studio Iguana | 2023.2.1 Canary 3 已修复的问题Android Gradle 插件 问题 295205663 将 AGP 从 8.0.2 更新到 8.1.0 后,任务“:app:mergeReleaseClasses”执行失败 问题 298008231 [Gradle 8.4][升级] 由于使用 kotlin gradle 插件中已废弃的功能&#…...

iOS17 隐私协议适配详解
1. 背景 网上搜了很多文章,总算有点头绪了。其实隐私清单最后做出来就是一个plist文件。找了几个常用三方已经配好的看了看,比着做就好了。 WWDC23 中关于隐私部分的更新(WWDC23 隐私更新官网),其中提到了第三方 SDK 的…...

LeetCode 每日一题 Day 116-122
2580. 统计将重叠区间合并成组的方案数 给你一个二维整数数组 ranges ,其中 ranges[i] [starti, endi] 表示 starti 到 endi 之间(包括二者)的所有整数都包含在第 i 个区间中。 你需要将 ranges 分成 两个 组(可以为空…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...