首页 > 其他分享 >vue-富文本/自定义上传图片

vue-富文本/自定义上传图片

时间:2023-11-17 16:44:32浏览次数:38  
标签:picker vue 自定义 data snow value 上传 ql before

1、下载组件

npm install vue-quill-editor –D

2、在需要的组件内引入富文本

import { quillEditor } from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'

components: {
    quillEditor
  }

<quill-editor
        ref="QuillEditor"
        v-model="editorContent"
        class="myQuillEditor"
        :options="editorOption"
        @change="editorChange($event)">
        </quill-editor>

3、基本配置

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 {
  props: {
    editorContent: String
  },
  components: {
    Upload,
    quillEditor
  },
  data () {
    return {
      editorOption: {
        theme: 'snow',
        modules: {
          toolbar: {
            container: toolbarOptions,  // 工具栏
            handlers: {
                'image': function (value) {
                    if (value) {
                        document.querySelector('.el-upload-dragger button').click()
                    } else {
                        this.quill.format('image', false);
                    }
                }
            }
          }
        },
        placeholder: '请输入...',
        readOnly: false
      },
      // 七牛
      qiNiuObj: {
        header: {
          Authorization: ''
        },
        label: '',
        limit: 100,
        accept: '.jpg,.png',
        btnName: '上传图片',
        QiniuData: {
          key: "", //图片名字处理
          token: "" //七牛云token
        },
        domain: "", // 七牛云的上传地址
        qiniuaddr: "", // 七牛云的图片外链地址
        uploadPicUrl: "", //提交到后台图片地址
        fileList: [] // 图片列表
      }
    }
  },
  methods: {
    editorChange ({editor,html,text}) {
      this.$emit('editorChange', {html,text})
    },
    // 七牛,
    getQiNiuUrl (obj) {
      this.qiNiuObj.uploadPicUrl = obj.uploadUrl.join(',')
      // 富文本内图片
      let quill = this.$refs.QuillEditor.quill
      let length = quill.getSelection().index;
      quill.insertEmbed(length, 'image', obj.response)
      quill.setSelection(length + 1)
    },
    async getQiNiu() { // 获取七牛云token
      const res = await this.$api.hotelHost.getQiNiu({})
      if (res.data.code == 200) {
        this.qiNiuObj.QiniuData.token = res.data.data.token
        this.qiNiuObj.domain = res.data.data.upHost
        this.qiNiuObj.qiniuaddr = res.data.data.domain
      } else {
        this.$message.error(res.data.msg)
      }
    },
  },
  computed: {

  },
  watch: {

  },
  created () {
    this.getQiNiu()
  },
  mounted () {

  },
}
基本配置

4、css配置

.editor {
  line-height: normal !important;
  width: 930px;
  margin: 0 auto;
}
.ql-container {
  height: 400px !important;
}
.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: '12px';
}
.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: '等宽字体';
}
.Editor__upload{
  width: 0;
  height: 0;
  overflow: hidden;
  opacity: 0;
  margin-bottom: 0!important;
}
css配置

5、全部代码

<!-- 富文本编辑器 -->
<template>
  <div class=''>
    <el-card>
      <Upload :qiNiuObj="qiNiuObj" @changeUrl="getQiNiuUrl" class="Editor__upload"></Upload>
      <quill-editor
        ref="QuillEditor"
        v-model="editorContent"
        class="myQuillEditor"
        :options="editorOption"
        @change="editorChange($event)">
        </quill-editor>
    </el-card>
  </div>
</template>

<script>
import Upload from '@components/Upload.vue'
import { quillEditor } from 'vue-quill-editor'
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'

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 {
  props: {
    editorContent: String
  },
  components: {
    Upload,
    quillEditor
  },
  data () {
    return {
      editorOption: {
        theme: 'snow',
        modules: {
          toolbar: {
            container: toolbarOptions,  // 工具栏
            handlers: {
                'image': function (value) {
                    if (value) {
                        document.querySelector('.el-upload-dragger button').click()
                    } else {
                        this.quill.format('image', false);
                    }
                }
            }
          }
        },
        placeholder: '请输入...',
        readOnly: false
      },
      // 七牛
      qiNiuObj: {
        header: {
          Authorization: ''
        },
        label: '',
        limit: 100,
        accept: '.jpg,.png',
        btnName: '上传图片',
        QiniuData: {
          key: "", //图片名字处理
          token: "" //七牛云token
        },
        domain: "", // 七牛云的上传地址
        qiniuaddr: "", // 七牛云的图片外链地址
        uploadPicUrl: "", //提交到后台图片地址
        fileList: [] // 图片列表
      }
    }
  },
  methods: {
    editorChange ({editor,html,text}) {
      this.$emit('editorChange', {html,text})
    },
    // 七牛,
    getQiNiuUrl (obj) {
      this.qiNiuObj.uploadPicUrl = obj.uploadUrl.join(',')
      // 富文本内图片
      let quill = this.$refs.QuillEditor.quill
      let length = quill.getSelection().index;
      quill.insertEmbed(length, 'image', obj.response)
      quill.setSelection(length + 1)
    },
    async getQiNiu() { // 获取七牛云token
      const res = await this.$api.hotelHost.getQiNiu({})
      if (res.data.code == 200) {
        this.qiNiuObj.QiniuData.token = res.data.data.token
        this.qiNiuObj.domain = res.data.data.upHost
        this.qiNiuObj.qiniuaddr = res.data.data.domain
      } else {
        this.$message.error(res.data.msg)
      }
    },
  },
  computed: {

  },
  watch: {

  },
  created () {
    this.getQiNiu()
  },
  mounted () {

  },
}
</script>

<style lang='scss'>
.editor {
  line-height: normal !important;
  width: 930px;
  margin: 0 auto;
}
.ql-container {
  height: 400px !important;
}
.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: '12px';
}
.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: '等宽字体';
}
.Editor__upload{
  width: 0;
  height: 0;
  overflow: hidden;
  opacity: 0;
  margin-bottom: 0!important;
}
</style>
Editor组件

标签:picker,vue,自定义,data,snow,value,上传,ql,before
From: https://www.cnblogs.com/WebMadman/p/12096461.html

相关文章

  • Vue3 模板引用 ref 的实现原理
    什么是模板引用ref?有时候可以使用 ref attribute为子组件或HTML元素指定引用ID。<template><inputref="input"/></template><script>import{defineComponent,ref}from"vue";exportdefaultdefineComponent({setup(){......
  • Vue3 的 effect、 watch、watchEffect 的实现原理
    所谓watch,就是观测一个响应式数据或者监测一个副作用函数里面的响应式数据,当数据发生变化的时候通知并执行相应的回调函数。Vue3最新的watch实现是通过最底层的响应式类ReactiveEffect的实例化一个reactiveeffect对象来实现的。它的创建过程跟effectAPI的实现类似,所......
  • Vue插槽(Slot)的实现原理
    实现原理(简单文字)slot又名插槽,是Vue的内容分发机制,组件内部的模板引擎使用slot元素作为承载分发内容的出口。插槽slot是子组件的一个模板标签元素,而这一个标签元素是否显示,以及怎么显示是由父组件决定的。slot又分三类,默认插槽,具名插槽和作用域插槽实现原理:当子组件vm......
  • 实现多个大文件拖拽上传+大文件分片上传+断点续传+文件预览
    技术关键词前端:@vue/cli-service+element-ui+axios后端:node.js+koa思路分析拖拽上传拖拽上传是利用HTML5新特性实现拖拽上传,详细用法可阅读MDN-drag利用dragover事件(当某物被拖动的对象在另一对象容器范围内拖动时触发此事件)和drop事件(在一个拖动过程中,释放鼠标键时......
  • vuex与redux比较
    相同点state共享数据流程一致:定义全局state,触发,修改state原理相似,通过全局注入store。不同点vuex定义了state、getter、mutation、action四个对象;redux定义了state、reducer、action。vuex触发方式有两种commit同步和dispatch异步;redux同步和异步都使用disp......
  • 手写基础vuex
    实现一个插件:声明Store类,挂载$storeStore具体实现:创建响应式的state,保存mutations、actions和getters实现commit根据用户传入type执行对应mutation实现dispatch根据用户传入type执行对应action,同时传递上下文实现getters,按照getters定义对state做派......
  • vue路由通过props传参
    方式一:布尔模式方法一:直接传参路由配置:{path:'/user/:id/:price',name:'User',component:User,props:true}页面A<template><div><router-link:to="{name:'User',params:{id:12320,price:'$10......
  • Vue $nextTick原理
    作用:vue更新DOM是异步更新的,数据变化,DOM的更新不会马上完成,nextTick的回调是在下次DOM更新循环结束之后执行的延迟回调。实现原理:nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用Promise:可以将函数延迟到当前函数调用栈最末端MutationObserver:是H5新加......
  • vue2为什么只重写了7个数组方法
    首先看源码//即将要被劫持的数组letarr=[1,2,3];//先把要劫持的方法列出来letmethods=["push","pop","shift","unshift","reverse","sort","splice"];//既然要劫持原型,就要先把原型拿过来letarrayProto=Array.prototy......
  • vue组件递归
    这样的场景:渲染列表数据的时候,列表的子项还是列表。如果层级少尚且可以用几个for循环搞定,但是层级多或者层级不确定就有点无从下手了。其实这就是树形结构数据,像常见的组织架构图,文件夹目录,导航菜单等都属于这种结构。很多组件库都带有树形组件,但往往样式不是想要的,改起来也非......