当前位置:   article > 正文

vue+element-ui 富文本编辑器_element 富文本编辑器

element 富文本编辑器
  1. npm install vue-quill-editor --save
  2. npm install quill-image-drop-module --save
  3. npm install quill-image-resize-module --save

 2.在组件下面新增组件 QlEditor

3.index.vue

  1. <template>
  2. <div>
  3. <div id='quillEditorQiniu'>
  4. <!-- 基于elementUi的上传组件 el-upload begin-->
  5. <el-upload
  6. class="avatar-uploader"
  7. :action="uploadImgUrl"
  8. :accept="'image/*,video/*'"
  9. :show-file-list="false"
  10. :on-success="uploadEditorSuccess"
  11. :on-error="uploadEditorError"
  12. :before-upload="beforeEditorUpload"
  13. :headers="headers">
  14. </el-upload>
  15. <!-- 基于elementUi的上传组件 el-upload end-->
  16. <quill-editor
  17. class="editor"
  18. v-model="content"
  19. ref="customQuillEditor"
  20. :options="editorOption"
  21. @blur="onEditorBlur($event)"
  22. @focus="onEditorFocus($event)"
  23. @change="onEditorChange($event)"
  24. >
  25. </quill-editor>
  26. </div>
  27. </div>
  28. </template>
  29. <script>
  30. import Vue from 'vue'
  31. import { Quill } from 'vue-quill-editor'
  32. import { ImageDrop } from 'quill-image-drop-module'
  33. import ImageResize from 'quill-image-resize-module'
  34. Quill.register('modules/imageDrop', ImageDrop)
  35. Quill.register('modules/imageResize', ImageResize)
  36. // //引入quill-editor编辑器
  37. // import VueQuillEditor from 'vue-quill-editor'
  38. // import 'quill/dist/quill.core.css'
  39. // import 'quill/dist/quill.snow.css'
  40. // import 'quill/dist/quill.bubble.css'
  41. // Vue.use(VueQuillEditor)
  42. // //实现quill-editor编辑器拖拽上传图片
  43. // import Quill from 'quill'
  44. // import { ImageDrop } from 'quill-image-drop-module'
  45. // Quill.register('modules/imageDrop', ImageDrop)
  46. // //实现quill-editor编辑器调整图片尺寸
  47. // import ImageResize from 'quill-image-resize-module'
  48. // Quill.register('modules/imageResize', ImageResize)
  49. // 这里引入修改过的video模块并注册
  50. import Video from './video'
  51. Quill.register(Video, true)
  52. //获取登录token,引入文件,如果只是简单测试,没有上传文件是否登录的限制的话,
  53. //这个token可以不用获取,文件可以不引入,把上面对应的上传文件携带请求头 :headers="headers" 这个代码删掉即可
  54. import {getToken} from "@/utils/auth";
  55. const toolbarOptions = [
  56. ['bold', 'italic', 'underline', 'strike'], // toggled buttons
  57. ['blockquote', 'code-block'],
  58. [{'header': 1}, {'header': 2}], // custom button values
  59. [{'list': 'ordered'}, {'list': 'bullet'}],
  60. [{'script': 'sub'}, {'script': 'super'}], // superscript/subscript
  61. [{'indent': '-1'}, {'indent': '+1'}], // outdent/indent
  62. [{'direction': 'rtl'}], // text direction
  63. [{'size': ['small', false, 'large', 'huge']}], // custom dropdown
  64. [{'header': [1, 2, 3, 4, 5, 6, false]}],
  65. [{'color': []}, {'background': []}], // dropdown with defaults from theme
  66. [{'font': []}],
  67. [{'align': []}],
  68. ['link', 'image', 'video'],
  69. ['clean'] // remove formatting button
  70. ];
  71. export default {
  72. name:"QlEditor",
  73. props: {
  74. /* 编辑器的内容 */
  75. value: {
  76. type: String,
  77. default: "",
  78. },
  79. /* 高度 */
  80. height: {
  81. type: Number,
  82. default: null,
  83. },
  84. /* 最小高度 */
  85. minHeight: {
  86. type: Number,
  87. default: null,
  88. },
  89. /* 只读 */
  90. readOnly: {
  91. type: Boolean,
  92. default: false,
  93. },
  94. /* 上传文件大小限制(MB) */
  95. fileSize: {
  96. type: Number,
  97. default: 5,
  98. },
  99. },
  100. model: {
  101. //v-model本质上是一个语法糖 这一步在父组件中可使用v-model
  102. prop: "value",
  103. event: "change",
  104. },
  105. data(){
  106. return {
  107. headers: {
  108. Authorization: "Bearer " + getToken(),
  109. token: getToken()
  110. },
  111. uploadImgUrl:process.env.VUE_APP_BASE_API + "/admin/article/uploadFile",
  112. uploadUrlPath:"没有文件上传",
  113. quillUpdateImg:false,
  114. content:'', //最终保存的内容
  115. editorOption:{
  116. placeholder:'请输入内容',
  117. modules: {
  118. imageResize: {
  119. displayStyles: {
  120. backgroundColor: 'black',
  121. border: 'none',
  122. color: 'white'
  123. },
  124. modules: [ 'Resize', 'DisplaySize', 'Toolbar' ]
  125. },
  126. toolbar: {
  127. container: toolbarOptions, // 工具栏
  128. handlers: {
  129. 'image': function (value) {
  130. if (value) {
  131. document.querySelector('#quillEditorQiniu .avatar-uploader input').click()
  132. } else {
  133. this.quill.format('image', false);
  134. }
  135. },
  136. 'video': function (value) {
  137. if (value) {
  138. document.querySelector('#quillEditorQiniu .avatar-uploader input').click()
  139. } else {
  140. this.quill.format('video', false);
  141. }
  142. },
  143. }
  144. }
  145. }
  146. },
  147. }
  148. },
  149. methods:{
  150. //上传图片之前async
  151. beforeEditorUpload(res, file){
  152. //显示上传动画
  153. this.quillUpdateImg = true;
  154. // const res1 = await uploadImage()
  155. // console.log(res1,'=====');
  156. // this.$emit('before',res, file)
  157. console.log(res);
  158. console.log(file);
  159. },
  160. // 上传图片成功
  161. uploadEditorSuccess(res, file) {
  162. console.log('res11',res);
  163. console.log('file22',file);
  164. //拼接出上传的图片在服务器的完整地址
  165. let url=res.url;
  166. let type=url.substring(url.lastIndexOf(".")+1);
  167. // 获取富文本组件实例
  168. let quill = this.$refs.customQuillEditor.quill;
  169. // 获取光标所在位置
  170. let length = quill.getSelection().index;
  171. // 插入图片||视频 res.info为服务器返回的图片地址
  172. if(type=='mp4' || type=='MP4'){
  173. window.jsValue=url;
  174. quill.insertEmbed(length, 'video', url)
  175. }else{
  176. quill.insertEmbed(length, 'image', url)
  177. }
  178. // 调整光标到最后
  179. quill.setSelection(length + 1)
  180. //取消上传动画
  181. this.quillUpdateImg = false;
  182. },
  183. // 上传(文件)图片失败
  184. uploadEditorError(res, file) {
  185. //页面提示
  186. this.$message.error('上传图片失败')
  187. //取消上传动画
  188. this.quillUpdateImg = false;
  189. },
  190. //上传组件返回的结果
  191. uploadResult:function (res){
  192. console.log('jg',res)
  193. this.uploadUrlPath=res;
  194. },
  195. // 失去焦点事件
  196. onEditorBlur(quill) {
  197. console.log('editor blur!', quill)
  198. },
  199. // 获得焦点事件
  200. onEditorFocus(quill) {
  201. console.log('editor focus!', quill)
  202. },
  203. // 准备富文本编辑器
  204. onEditorReady(quill) {
  205. console.log('editor ready!', quill)
  206. },
  207. // 内容改变事件
  208. onEditorChange({ quill, html, text }) {
  209. console.log('editor change!', quill, html, text)
  210. this.content = html
  211. }
  212. },
  213. created () {
  214. },
  215. //只执行一次,加载执行
  216. mounted () {
  217. console.log("开始加载")
  218. // 初始给编辑器设置title
  219. },
  220. watch: {
  221. value: {
  222. handler(val) {
  223. if (val !== this.content) {
  224. this.content = val === null ? "" : val;
  225. }
  226. },
  227. immediate: true,
  228. },
  229. },
  230. }
  231. </script>
  232. <style>
  233. .editor {
  234. line-height: normal !important;
  235. height: 400px;
  236. margin-bottom: 50px;
  237. }
  238. .ql-snow .ql-tooltip[data-mode="link"]::before {
  239. content: "请输入链接地址:";
  240. }
  241. .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
  242. border-right: 0px;
  243. content: "保存";
  244. padding-right: 0px;
  245. }
  246. .ql-snow .ql-tooltip[data-mode="video"]::before {
  247. content: "请输入视频地址:";
  248. }
  249. .ql-snow .ql-picker.ql-size .ql-picker-label::before,
  250. .ql-snow .ql-picker.ql-size .ql-picker-item::before {
  251. content: "14px";
  252. }
  253. .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
  254. .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
  255. content: "10px";
  256. }
  257. .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
  258. .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
  259. content: "18px";
  260. }
  261. .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
  262. .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
  263. content: "32px";
  264. }
  265. .ql-snow .ql-picker.ql-header .ql-picker-label::before,
  266. .ql-snow .ql-picker.ql-header .ql-picker-item::before {
  267. content: "文本";
  268. }
  269. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
  270. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
  271. content: "标题1";
  272. }
  273. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
  274. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
  275. content: "标题2";
  276. }
  277. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
  278. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
  279. content: "标题3";
  280. }
  281. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
  282. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
  283. content: "标题4";
  284. }
  285. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
  286. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
  287. content: "标题5";
  288. }
  289. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
  290. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
  291. content: "标题6";
  292. }
  293. .ql-snow .ql-picker.ql-font .ql-picker-label::before,
  294. .ql-snow .ql-picker.ql-font .ql-picker-item::before {
  295. content: "标准字体";
  296. }
  297. .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
  298. .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
  299. content: "衬线字体";
  300. }
  301. .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
  302. .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
  303. content: "等宽字体";
  304. }
  305. </style>

4.video.js

  1. import { Quill } from 'vue-quill-editor'
  2. // 源码中是import直接倒入,这里要用Quill.import引入
  3. const BlockEmbed = Quill.import('blots/block/embed')
  4. // const Link = Quill.import('formats/link')
  5. const ATTRIBUTES = ['height', 'width']
  6. class Video extends BlockEmbed {
  7. static create (value) {
  8. const node = super.create(value)
  9. // console.log("js文件"+ window.jsValue)
  10. // 添加video标签所需的属性
  11. node.setAttribute('controls', 'controls') // 控制播放器
  12. //删除原生video的控制条的下载或者全屏按钮的方法
  13. //<video controls controlsList='nofullscreen nodownload noremote footbar' ></video>
  14. //不用哪个在下面加上哪个
  15. node.setAttribute('controlsList', 'nofullscreen') // 控制删除
  16. node.setAttribute('type', 'video/mp4')
  17. node.setAttribute('style', 'object-fit:fill;width: 100%;')
  18. node.setAttribute('preload', 'auto') // auto - 当页面加载后载入整个视频 meta - 当页面加载后只载入元数据 none - 当页面加载后不载入视频
  19. node.setAttribute('playsinline', 'true')
  20. node.setAttribute('x-webkit-airplay', 'allow')
  21. // node.setAttribute('x5-video-player-type', 'h5') // 启用H5播放器,是wechat安卓版特性
  22. node.setAttribute('x5-video-orientation', 'portraint') // 竖屏播放 声明了h5才能使用 播放器支付的方向,landscape横屏,portraint竖屏,默认值为竖屏
  23. node.setAttribute('x5-playsinline', 'true') // 兼容安卓 不全屏播放
  24. node.setAttribute('x5-video-player-fullscreen', 'true') // 全屏设置,设置为 true 是防止横屏
  25. node.setAttribute('src', window.jsValue)
  26. return node
  27. }
  28. static formats (domNode) {
  29. return ATTRIBUTES.reduce((formats, attribute) => {
  30. if (domNode.hasAttribute(attribute)) {
  31. formats[attribute] = domNode.getAttribute(attribute)
  32. }
  33. return formats
  34. }, {})
  35. }
  36. // static sanitize (url) {
  37. //
  38. // // eslint-disable-line import/no-named-as-default-member
  39. // }
  40. static value (domNode) {
  41. return domNode.getAttribute('src')
  42. }
  43. format (name, value) {
  44. if (ATTRIBUTES.indexOf(name) > -1) {
  45. if (value) {
  46. this.domNode.setAttribute(name, value)
  47. } else {
  48. this.domNode.removeAttribute(name)
  49. }
  50. } else {
  51. super.format(name, value)
  52. }
  53. }
  54. html () {
  55. const { video } = this.value()
  56. return `<a href="${video}" rel="external nofollow" >${video}</a>`
  57. }
  58. }
  59. Video.blotName = 'video' // 这里不用改,楼主不用iframe,直接替换掉原来,如果需要也可以保留原来的,这里用个新的blot
  60. Video.className = 'ql-video'
  61. Video.tagName = 'video' // 用video标签替换iframe
  62. export default Video

5.

1、在根目录下新建vue.config.js 文件 (不是在src 下)

var webpack = require('webpack');

module.exports = {

  // 在vue.config.js中configureWebpack中配置

// 要引入webpack

configureWebpack: {

    plugins: [

      new webpack.ProvidePlugin({

        'window.Quill': 'quill/dist/quill.js',

        'Quill': 'quill/dist/quill.js'

      }),

    ]

  }

}

6.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);

7.页面使用

  1. <el-form-item label="内容">
  2. <QlEditor v-model="temp.content" ref="qlEditorRef" :min-height="192"/>
  3. </el-form-item>
  4. <script>
  5. /** 提交按钮 */
  6. submitForm() {
  7. this.$refs["form"].validate(valid => {
  8. this.temp.content=this.$refs.qlEditorRef.content; //获取富文本的内容
  9. });
  10. },
  11. </script>

8.需要重新运行一下,不然会报错

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/883888
推荐阅读
相关标签
  

闽ICP备14008679号