一、不带图片的富文本

1、安装

npm install @vueup/vue-quill@alpha --save

2、引入

main.js中全局引入

import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css';

app.component('QuillEditor', QuillEditor)

3、使用组件

 <quill-editor :disabled="true" theme="snow" content-type="html" enable :content="desc" />

4、添加样式

<style scoped>
/deep/ .el-form-item__content {
  display: inline
}
</style>

5、效果如下:

二、带图片的富文本(图片存储为base64编码)

1、安装

npm install @vueup/vue-quill@alpha --save
npm install quill-image-extend-module --save

2、创建quillTool.js(用于添加超链接、视频)

在utils目录下创建quilTool.js文件,内容如下:

import { Quill } from '@vueup/vue-quill'
// 源码中是import直接倒入,这里要用Quill.import引入
const BlockEmbed = Quill.import('blots/block/embed')
const Link = Quill.import('formats/link')

const ATTRIBUTES = ['height', 'width']

class quillTool extends BlockEmbed {
    static create(value) {
        const node = super.create(value)
        // 添加video标签所需的属性
        node.setAttribute('controls', 'controls')
        node.setAttribute('type', 'video/mp4')
        node.setAttribute('src', this.sanitize(value))
        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 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}">${video}</a>`
    }
}
quillTool.blotName = 'video' // 这里不用改,楼主不用iframe,直接替换掉原来,如果需要也可以保留原来的,这里用个新的blot
quillTool.className = 'ql-video'
quillTool.tagName = 'video' // 用video标签替换iframe

export default quillTool

3、页面代码

template

<el-form-item label="内容:" prop="content" >
          <QuillEditor ref="quill-editor" v-model:content="entity.content" :options="editorOption" contentType="html" style="margin-bottom: 10px" />
        </el-form-item>

js

<script>

// 工具栏配置
const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线
  ["blockquote", "code-block"], // 引用
  [{ 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'], // 清除文本格式
  ['link', 'image', 'video'] // 链接、图片、视频
]
import {QuillEditor, Quill } from '@vueup/vue-quill'
import { container, ImageExtend, QuillWatch } from 'quill-image-extend-module'
import quillTool from '@/utils/quillTool'
Quill.register(quillTool, true)
Quill.register('modules/ImageExtend', ImageExtend)
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
export default {
  name: 'EditDictDialog',
  components: { QuillEditor },
  data() {
    return {
      entity:{
        title: null,
        content: null
      },
      submitLoading:false,
      dialogTableVisible:false,
      titleMap:{
        'create':"新增",
        'update':"修改"
      },
      titleStatus:'create',
     rules:{
       title:[
         { required: true,message:'请输入概况标题',  trigger: 'blur' },
         { min: 1, max: 100, message: '长度在100个字符以内', trigger: 'blur' }
       ]
     },
      isDisabledName: false,
      editorOption: {
        theme: 'snow',
        placeholder: '请输入',
        modules: {
          // 处理点击工具栏图片按钮,上传图片base64位转换成服务器图片url
          ImageExtend: {
            loading: true, // 可选参数 是否显示上传进度和提示语
            name: 'file_name', // 参数名
            action: '', // 服务器地址,如果为空则采用base64插入图片
            headers: xhr => { // 设置请求头参数(选填)
              xhr.setRequestHeader('Content-Type', 'multipart/form-data')
            },
            response: res => {
              console.log(res)
              return res.data.imgPath
            },
            size: 8, // 可选参数 图片大小,图片不能超过8M
            sizeError: () => {
              this.$message.error('粘贴图片大小不能超过8MB!')
            }
          },
          toolbar: {
            container: toolbarOptions,
            handlers: {
              image: function(value) {
                QuillWatch.emit(this.quill.id)
              },
              link: function(value) {
                if (value) {
                  var href = prompt('请输入链接地址:')
                  this.quill.format('link', href)
                } else {
                  this.quill.format('link', false)
                }
              },
              video: function(value) {
                if (value) {
                  var href = prompt('请输入视频链接:')
                  this.quill.format('video', href)
                } else {
                  this.quill.format('video', false)
                }
              }
            }
          }
        }
      }
    }
  },
  methods: {
    openEditDialog(dialogStatus,titleStatus, row) {
      this.isDisabledName = false
      this.dialogTableVisible=dialogStatus;
      this.titleStatus=titleStatus;
      if(titleStatus==="create"){
        this.cleanData();
      } else if (titleStatus==="update") { // 更新不校验饮片名称是否存在
        this.isDisabledName = true
      }
      if(row){
        this.entity=Object.assign({},row);
        this.$nextTick(() => {
          this.$refs['quill-editor'].setHTML(this.entity.content)
        })
      }
      this.$nextTick(() => {
        this.$refs["form"].clearValidate();
      })

    },

    close(){
      this.dialogTableVisible=false;
      this.cleanData();
      this.titleStatus = 'create'
      this.$nextTick(() => {
        this.$refs['quill-editor'].setHTML('')
        this.$refs['form'].clearValidate()
      })
    }

  }
}
</script>

style

<style scoped>
/deep/ .el-form-item__content {
  display: inline
}
/deep/ .el-dialog__footer{
  margin-top: 10px;
}
/deep/ .ql-container {
  height: 300px;
  line-height: normal;
  width: auto;
}

/deep/ span.ql-size {
  max-width: 80px !important;
}

/deep/ .ql-tooltip[data-mode="link"]::before {
  content: "请输入链接地址:";
}

/deep/ .ql-tooltip.ql-editing a.ql-action::after {
  border-right: 0px;
  content: "保存";
  padding-right: 0px;
}

/deep/ .ql-tooltip[data-mode="video"] {
  left: 0 !important;
}

/deep/ .ql-tooltip[data-mode="video"]::before {
  content: "请输入视频地址:";
}

/deep/ .ql-picker.ql-size .ql-picker-label::before,
.ql-picker.ql-size .ql-picker-item::before {
  content: "14px";
}

/deep/ .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
.ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
  content: "10px";
}

/deep/ .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
.ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
  content: "18px";
}

/deep/ .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
.ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
  content: "32px";
}

/deep/ .ql-picker.ql-header .ql-picker-label::before,
.ql-picker.ql-header .ql-picker-item::before {
  content: "文本";
}

/deep/ .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
  content: "标题1";
}

/deep/ .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
  content: "标题2";
}

/deep/ .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
  content: "标题3";
}

/deep/ .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
  content: "标题4";
}

/deep/ .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
  content: "标题5";
}

/deep/ .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
  content: "标题6";
}

/deep/ .ql-picker.ql-font .ql-picker-label::before,
.ql-picker.ql-font .ql-picker-item::before {
  content: "标准字体";
}

/deep/ .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
.ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
  content: "衬线字体";
}

/deep/ .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
  content: "等宽字体";
}
</style>

4、效果

添加图片

点击确定后保存到数据库中,该字段为longtext类型,图片以base64编码进行保存。

二、带图片的富文本(图片存储url路径)

由于图片存储为base64编码时,从后台获取图片的话,由于内容太多,导致微信开发者工具无法接收如此大的数据,故改为图片通过url存储。

enctype:规定了form表单在发送到服务器时候编码方式,它有如下的三个值。

application/x-www-form-urlencoded(表单):默认的编码方式。但是在用文本的传输和MP3等大型文件的时候,使用这种编码就显得 效率低下。

multipart/form-data(formData):指定传输数据为二进制类型,比如图片、mp3、文件。

text/plain:纯文体的传输。空格转换为 “+” 加号,但不对特殊字符编码。

注意:图片是以base64编码的方式存储还是以url的方式存储取决于上传图片是是否请求后台上传图片的接口,即如果action值为空则采用base64插入图片,此时需要指定请求头multipart/form-data

headers: xhr => { // 设置请求头参数(选填)
              xhr.setRequestHeader('Content-Type', 'multipart/form-data')
            },

如果action为后台的图片上传接口uri,如/api/fileUpload/uploadImage,即上传图片时要请求后台的接口,此时要注释掉请求头:

// headers: xhr => { // 设置请求头参数(选填)
            //   xhr.setRequestHeader('Content-Type', 'multipart/form-data')
            // },

否则后台报错:the request was rejected because no multipart boundary was found

部分代码修改如下:

editorOption: {
        theme: 'snow',
        placeholder: '请输入',
        modules: {
          // 处理点击工具栏图片按钮,上传图片base64位转换成服务器图片url
          ImageExtend: {
            loading: true, // 可选参数 是否显示上传进度和提示语
            name: 'file', // 参数名,与后台接收的参数名保持一致
            action: '/api/fileUpload/uploadImage', // 服务器地址,如果为空则采用base64插入图片
            // headers: xhr => { // 设置请求头参数(选填)
            //   xhr.setRequestHeader('Content-Type', 'multipart/form-data')
            // },
            response: res => {
              console.log(res)
              // return res.data.imgPath
              return 'http://192.168.10.114:8012/upload/' + res.data.imgPath
            },
            size: 8, // 可选参数 图片大小,图片不能超过8M
            sizeError: () => {
              this.$message.error('粘贴图片大小不能超过8MB!')
            }
          },
          toolbar: {
            container: toolbarOptions,
            handlers: {
              image: function(value) {
                QuillWatch.emit(this.quill.id)
              },
              link: function(value) {
                if (value) {
                  var href = prompt('请输入链接地址:')
                  this.quill.format('link', href)
                } else {
                  this.quill.format('link', false)
                }
              },
              video: function(value) {
                if (value) {
                  var href = prompt('请输入视频链接:')
                  this.quill.format('video', href)
                } else {
                  this.quill.format('video', false)
                }
              }
            }
          }
        }
      },

注意:配置http://192.168.10.114:8012/upload/是为了访问后台时映射本地电脑的图片。

配置addResourceHandler和addResourceLocations来访问本地的图片

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class FilePathConfig implements WebMvcConfigurer {

    @Value("${system.file.uploadPath}")
    private String filePath;

    public FilePathConfig() {
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        this.filePath.replace("\\", "/");
        this.filePath = this.filePath + "/";
        registry.addResourceHandler("/upload/**").addResourceLocations("file:" + filePath);
    }
}

配置文件

#文件上传存储的路径
system.file.uploadPath=d:/data/cmdptcp/fileUpload

上传图片时,后台接收的controller

@PostMapping("/uploadImage")
    @Log("图片上传")
    public Result uploadImage(@RequestParam("file") MultipartFile multipartFile) {
        return fileUploadService.uploadImage(multipartFile);
    }

注意:参数名file与前端保持一致,否则无法接收到请求。

三、更改插入图片大小

(1)、更改插入图片大小、  安装

npm install quill
npm install quill-image-resize-module --save
npm install quill-image-drop-module --save

(2)、在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);

(3)、在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'
      })
    ]
  }
};

(4)、editorOption中添加如下内容

editorOption: {
        theme: 'snow',
        placeholder: '请输入',
        modules: {
          ImageExtend: {
            loading: true,
            name: 'file',
            action: imageUploadApi,
            // headers: xhr => {
            //   xhr.setRequestHeader('Content-Type', 'multipart/form-data')
            // },
            response: res => {
              console.info(res)
              return quillImageHost + '/upload/' + res.data.imgPath
            },
            size: 8,
            sizeError: () => {
              this.$message.error('粘贴图片大小不能超过8MB!')
            }
          },
          toolbar: {
            container: toolbarOptions,
            handlers: {
              image: function () {
                QuillWatch.emit(this.quill.id)
              },
              link: function (value) {
                if (value) {
                  let href = prompt('请输入链接地址:');
                  this.quill.format('link', href);
                } else {
                  this.quill.format('link', false)
                }
              },
              video: function (value) {
                if (value) {
                  let href = prompt('请输入视频地址:')
                  this.quill.format('video', href)
                } else {
                  this.quill.format('video', false)
                }
              },
            }
          },
          // 如果使用图片可调整大小,加入如下配置
          imageResize: {
            displayStyles: {
              backgroundColor: 'black',
              border: 'none',
              color: 'white'
            },
            modules: ['Resize', 'DisplaySize', 'Toolbar']
          }
        }
      },

 

posted on 2022-09-04 12:49  周文豪  阅读(5872)  评论(0编辑  收藏  举报