富文本使用补充

安装vue-quill-editor

npm install vue-quill-editor --save

注意:
1. 一定不要忘了 -save ,否则的话可能会导致富文本框加载失败
2. 安装完成之后,查看package.json文件中的dependencies里是否有安装好的vue-quill-editor这个插件,如果没有加--save,则dependencies中可能不会有这个插件,而是再devDependencies中,这个时候浏览器会报错,因为该功能已经开发很久了,报错信息暂时无法提供

 

在vue中如果需要对图片进行缩放,需要使用该插件,注意需要将该插件通过webpack注册到全局中,才能进行正常使用(vue3.0中的注册方法,vue2.0自行百度)

chainWebpack: (config) => { config.plugin('provide').use(webpack.ProvidePlugin, [{ 'window.Quill': 'quill' }]); }

 

vue-quill-editor使用方法:(注释包含在里面)

  • npm安装vue-quill-editor(富文本编辑器)和quill-image-resize-module、quill-image-extend-module(图片缩放)
  • 注意如果需要修改editor中的样式,那么一定不要在style上面写scoped,只有在全局状态下修改editor样式才会生效,当前组件内修改是不会生效的
<template>
  <div class="editor">
    <input type="file" style="display:none;" id="input" @change.prevent="upLoad()"/>
    <quill-editor
      v-model="contentValue"
	  // 有时候样式会出现问题,是因为少加了ql-editor这个class,需要加上
      class="quill-editor ql-editor"
      ref="myQuillEditor"
      style="padding-top:0; display: flex; flex-direction: column;"
      :content="contentValue"
      :options="editorOption"
      @blur="onEditorBlur($event)"
      @focus="onEditorFocus($event)"
      @change="onEditorChange($event)"
    >
    </quill-editor>
    <span class="wordNumber">{{TiLength}}/4000</span>
  </div>
</template>

<script>
  import 'quill/dist/quill.snow.css' // 富文本编辑器外部引用样式  三种样式三选一引入即可
  //import 'quill/dist/quill.core.css'
  //import 'quill/dist/quill.bubble.css'
  import { quillEditor, Quill } from 'vue-quill-editor' // 调用富文本编辑器
  import { ImageExtend } from 'quill-image-extend-module'
  // quill-image-resize-module该插件是用于控制上传的图片的大小
  import ImageResize from 'quill-image-resize-module' 
  Quill.register('modules/imageResize', ImageExtend);
  Quill.register('modules/imageResize', ImageResize);

  export default {
    props: {
      content: {
        type: String,
        default: ''
      },
      disabled: {
        type: Boolean,
        default: false
      }
    },
    watch: {
      content: function(oldV, newV) {
        this.contentValue = oldV
      }
    },
    components: {
      quillEditor
    },
    data() {
      return {
        contentValue: '',
        flag: true, // 初始化富文本
        TiLength: 0,
        editorOption: {
          placeholder: "",
          modules: {
            toolbar: { // 菜单栏(工具栏),手动配置自己需要的富文本功能
              container: [
                ["bold", "italic", "underline", "strike"], //加粗,斜体,下划线,删除线
                [{ indent: "-1" }, { indent: "+1" }], // 左右缩进
                [{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
                [{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
                [{ align: [] }], //对齐方式
                ["image", /*"video"*/], //上传图片、上传视频
              ],
              handlers: {
                'image': function (value) { // 劫持图片上传,自定义图片上传
                  if (value) {
                      // 通过input的type=file唤醒选择弹框,选择之后自定义上传路径
                    document.querySelector('#input').click()
                  } else {
                    this.quill.format('image', false);
                  }

                }
              }
            },
            imageResize:{ // 图片缩放比例
              displayStyles:{
                  backgroundColor:'black',
                  border:'none',
                  color:'white'
              },
              modules:['Resize','DisplaySize', 'Toolbar'] // Resize 允许缩放, DisplaySize 缩放时显示像素 Toolbar 显示工具栏
            }
          }
        },
      }
    },
    computed: {
      editor() {
        return this.$refs.myQuillEditor.quill;
      }
    },
    created () {
      this.contentValue = this.content
    },
    mounted() {
      this.pastePic();
    },
    methods: {
      onEditorReady(editor) {
        // 准备编辑器
      },
      onEditorBlur() {}, // 失去焦点事件
      // 获得焦点事件
      onEditorFocus(e) {
        e.enable(!this.disabled); // 禁用文本框
      },
      // 内容改变事件
      onEditorChange(val) {
        if (this.flag) {
          this.flag = false
          setTimeout(()=>{
            this.$refs.myQuillEditor.quill.setSelection(val.html.length + 1)
            this.TiLength = this.TiLength=val.quill.getLength()-1
          },20)
        }
        this.$emit('editor-content', {value: val.html}) // 触发事件,将富文本框中的值传递出去
        this.contentValue = val.html
        val.quill.deleteText(4000,4);
        if(this.contentValue==''){
          this.TiLength=0
        }else{
          this.TiLength=val.quill.getLength()-1
        }

      },
      upLoad() {
        let files = window.event.target.files[0];
        if (files.size / 1024 / 1024 > 2) { // 定义上传的图片的大小
          // this.$message.warning('请上传小于5M的文件');
          return;
        }
        let fileType = ['.jpg', '.png'];
        let suffix = files.name.substring(
          files.name.lastIndexOf('.'),
          files.name.length
        );
        if (fileType.indexOf(suffix) === -1) {
          this.$message.warning('请上传格式为png、jpg的文件');
          return;
        }
        // 自定义上传路径,根据需求上传到对应的服务器,本公司使用的是上传到ali-oss
        this.handleUpload(files);
      },
      // 上传到阿里云服务器
      handleUpload(files) {
        this.ossUpload(files)
          .then((res) => { // 上传成功之后的回调
            // 获取光标所在位置
            let length = this.editor.getSelection().index;
            // 插入图片  res.info为服务器返回的图片地址
            this.editor.insertEmbed(length, 'image', res.url)
            // 调整光标到最后
            this.editor.setSelection(length + 1)
          })
          .catch((files) => {
            this.$message.warning('上传失败');
          });
      },
      // 截屏粘贴到富文本框
      pastePic() {
        // 页面加载之后就监听粘贴事件paste
        this.editor.root.addEventListener('paste', evt => {
          if (evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length) {
              evt.preventDefault();
              [].forEach.call(evt.clipboardData.files, file => {
                  if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
                      return;
                  }
                  this.handleUpload(file);
              });
          }
        }, false);
      },
    }
  }
</script>
// 注意style不能加scoped。否则修改的.editor的样式是无法生效的
<style lang="scss">
  .editor {
    position: relative;
    .ql-toolbar{
      border: 1px solid #CDCFD4;
      height: 40px;
      background: #F0F2F5;
    }
    .ql-editor{
      min-height:240px;
      padding: 0;
      padding-top: 15px;
      box-sizing: border-box;
    }
    .ql-container {
      img {
        display: block;
      }
    }
    .wordNumber {
      position: absolute;
      right: 13px;
      bottom: 15px;
      color: #666666;
      font-size: 14px;
    }
  }
</style>

 

使用方法:

<editor
	@editor-content="listenContent"
	:content="quillEditor"
	:disabled="isDisabled"
/>
// editor-content: 实时监听富文本框中输入的值
// content: 富文本框中的初始默认值
// disabled: 是否禁用富文本框输入

 

 

自己的实现

<template>
  <div class="editor">
    <input
      type="file"
      style="display: none"
      id="input"
      @change.prevent="upLoad()"
    />
    <quill-editor
      v-model="contentValue"
      class="quill-editor ql-editor"
      ref="myQuillEditor"
      style="padding-top: 0; display: flex; flex-direction: column"
      :content="contentValue"
      :options="editorOption"
      @blur="onEditorBlur($event)"
      @focus="onEditorFocus($event)"
      @change="onEditorChange($event)"
    >
    </quill-editor>
    <span class="wordNumber">{{ TiLength }}/4000</span>
  </div>
</template>

<script>
import "quill/dist/quill.snow.css"; // 富文本编辑器外部引用样式  三种样式三选一引入即可
//import 'quill/dist/quill.core.css'
//import 'quill/dist/quill.bubble.css'
import { quillEditor, Quill } from "vue-quill-editor"; // 调用富文本编辑器
import { ImageExtend } from "quill-image-extend-module";
// quill-image-resize-module该插件是用于控制上传的图片的大小
import ImageResize from "quill-image-resize-module";
Quill.register("modules/imageResize", ImageExtend);
Quill.register("modules/imageResize", ImageResize);

export default {
  props: {
    content: {
      type: String,
      default: "",
    },
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  watch: {
    content: function (oldV, newV) {
      this.contentValue = oldV;
    },
  },
  components: {
    quillEditor,
  },
  data() {
    return {
      contentValue: "",
      flag: true, // 初始化富文本
      TiLength: 0,
      editorOption: {
        placeholder: "",
        modules: {
          toolbar: {
            // 菜单栏(工具栏),手动配置自己需要的富文本功能
            container: [
              ["bold", "italic", "underline", "strike"], //加粗,斜体,下划线,删除线
              [{ indent: "-1" }, { indent: "+1" }], // 左右缩进
              [{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
              [{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
              [{ align: [] }], //对齐方式
              ["image" /*"video"*/], //上传图片、上传视频
            ],
            handlers: {
              image: function (value) {
                // 劫持图片上传,自定义图片上传
                if (value) {
                  // 通过input的type=file唤醒选择弹框,选择之后自定义上传路径
                  document.querySelector("#input").click();
                } else {
                  this.quill.format("image", false);
                }
              },
            },
          },
          imageResize: {
            // 图片缩放比例
            displayStyles: {
              backgroundColor: "black",
              border: "none",
              color: "white",
            },
            modules: ["Resize", "DisplaySize", "Toolbar"], // Resize 允许缩放, DisplaySize 缩放时显示像素 Toolbar 显示工具栏
          },
        },
      },
    };
  },
  computed: {
    editor() {
      return this.$refs.myQuillEditor.quill;
    },
  },
  created() {
    this.contentValue = this.content;
  },
  mounted() {
    this.pastePic();
  },
  methods: {
    onEditorReady(editor) {
      // 准备编辑器
    },
    onEditorBlur() {}, // 失去焦点事件
    // 获得焦点事件
    onEditorFocus(e) {
      e.enable(!this.disabled); // 禁用文本框
    },
    // 内容改变事件
    onEditorChange(val) {
      if (this.flag) {
        this.flag = false;
        setTimeout(() => {
          this.$refs.myQuillEditor.quill.setSelection(val.html.length + 1);
          this.TiLength = this.TiLength = val.quill.getLength() - 1;
        }, 20);
      }
      this.$emit("editor-content", { value: val.html }); // 触发事件,将富文本框中的值传递出去
      this.contentValue = val.html;
      val.quill.deleteText(4000, 4);
      if (this.contentValue == "") {
        this.TiLength = 0;
      } else {
        this.TiLength = val.quill.getLength() - 1;
      }
    },
    upLoad() {
      let files = window.event.target.files[0];
      if (files.size / 1024 / 1024 > 2) {
        // 定义上传的图片的大小
        // this.$message.warning('请上传小于5M的文件');
        return;
      }
      let fileType = [".jpg", ".png"];
      let suffix = files.name.substring(
        files.name.lastIndexOf("."),
        files.name.length
      );
      if (fileType.indexOf(suffix) === -1) {
        this.$message.warning("请上传格式为png、jpg的文件");
        return;
      }
      // 自定义上传路径,根据需求上传到对应的服务器,本公司使用的是上传到ali-oss
      this.handleUpload(files);
    },
    // 上传到阿里云服务器
    handleUpload(files) {
        //不走七牛的写法
      var reader = new FileReader();
      reader.onloadend = () => {
        let length = this.editor.getSelection().index;
        this.editor.insertEmbed(length, "image", reader.result);
       // 调整光标到最后
        this.editor.setSelection(length + 1);
      };
      reader.readAsDataURL(files);

      //走起牛的写法
      // this.ossUpload(files)
      //   .then((res) => {
      //     // 上传成功之后的回调
      //     // 获取光标所在位置
      //     let length = this.editor.getSelection().index;
      //     // 插入图片  res.info为服务器返回的图片地址
      //     this.editor.insertEmbed(length, "image", res.url);
      //     // 调整光标到最后
      //     this.editor.setSelection(length + 1);
      //   })
      //   .catch((files) => {
      //     this.$message.warning("上传失败");
      //   });
    },
    // 截屏粘贴到富文本框
    pastePic() {
      // 页面加载之后就监听粘贴事件paste
      this.editor.root.addEventListener(
        "paste",
        (evt) => {
          if (
            evt.clipboardData &&
            evt.clipboardData.files &&
            evt.clipboardData.files.length
          ) {
            evt.preventDefault();
            [].forEach.call(evt.clipboardData.files, (file) => {
              if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
                return;
              }
              this.handleUpload(file);
            });
          }
        },
        false
      );
    },
  },
};
</script>
<style lang="scss">
.editor {
  position: relative;
  .ql-toolbar {
    border: 1px solid #cdcfd4;
    height: 40px;
    background: #f0f2f5;
  }
  .ql-editor {
    min-height: 240px;
    padding: 0;
    padding-top: 15px;
    box-sizing: border-box;
  }
  .ql-container {
    img {
      display: block;
    }
  }
  .wordNumber {
    position: absolute;
    right: 13px;
    bottom: 15px;
    color: #666666;
    font-size: 14px;
  }
}
</style>

 

 

七牛云上传的写法

<template>
  <div class="editor">
    <!-- 图片上传功能 -->
    <el-upload
      :on-change="upLoad"
      :auto-upload="false"
      :loading="true"
      class="upload-btn"
      ref="upload"
      action
      :show-file-list="false"
    >
      <el-button
        style="display: none"
        id="input"
        slot="trigger"
        icon="el-icon-upload"
        size="small"
        type="primary"
        >上传图片</el-button
      >
    </el-upload>

    <quill-editor
      v-model="contentValue"
      class="quill-editor ql-editor"
      ref="myQuillEditor"
      style="padding-top: 0; display: flex; flex-direction: column"
      :content="contentValue"
      :options="editorOption"
      @blur="onEditorBlur($event)"
      @focus="onEditorFocus($event)"
      @change="onEditorChange($event)"
    >
    </quill-editor>
    <span class="wordNumber">{{ TiLength }}/4000</span>
  </div>
</template>

<script>
import { Message } from "element-ui";
import { message } from "ant-design-vue";
import CDNClient from "@frontend/cdn-uploader";
import "quill/dist/quill.snow.css"; // 富文本编辑器外部引用样式  三种样式三选一引入即可
//import 'quill/dist/quill.core.css'
//import 'quill/dist/quill.bubble.css'
import { getCdnToken, sendCdnPicture } from "../../../../../api/index";
import { quillEditor, Quill } from "vue-quill-editor"; // 调用富文本编辑器
import { ImageExtend } from "quill-image-extend-module";
// quill-image-resize-module该插件是用于控制上传的图片的大小
import ImageResize from "quill-image-resize-module";
Quill.register("modules/imageResize", ImageExtend);
Quill.register("modules/imageResize", ImageResize);

export default {
  props: {
    content: {
      type: String,
      default: "",
    },
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  watch: {
    content: function (oldV, newV) {
      this.contentValue = oldV;
    },
  },
  components: {
    quillEditor,
  },
  data() {
    return {
      picUrl: "",
      uptoken: "",
      contentValue: "",
      flag: true, // 初始化富文本
      TiLength: 0,
      editorOption: {
        placeholder: "",
        modules: {
          toolbar: {
            // 菜单栏(工具栏),手动配置自己需要的富文本功能
            container: [
              ["bold", "italic", "underline", "strike"], //加粗,斜体,下划线,删除线
              [{ indent: "-1" }, { indent: "+1" }], // 左右缩进
              [{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
              [{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
              [{ align: [] }], //对齐方式
              ["image" /*"video"*/], //上传图片、上传视频
            ],
            handlers: {
              image: function (value) {
                // 劫持图片上传,自定义图片上传
                if (value) {
                  console.log("点击上传按钮");
                  // 通过input的type=file唤醒选择弹框,选择之后自定义上传路径
                  document.querySelector("#input").click();
                } else {
                  this.quill.format("image", false);
                }
              },
            },
          },
          imageResize: {
            // 图片缩放比例
            displayStyles: {
              backgroundColor: "black",
              border: "none",
              color: "white",
            },
            modules: ["Resize", "DisplaySize", "Toolbar"], // Resize 允许缩放, DisplaySize 缩放时显示像素 Toolbar 显示工具栏
          },
        },
      },
    };
  },
  computed: {
    editor() {
      return this.$refs.myQuillEditor.quill;
    },
  },
  created() {
    this.contentValue = this.content;
    this.getToken();
  },
  mounted() {
    this.pastePic();
  },
  methods: {
    onEditorReady(editor) {
      // 准备编辑器
    },
    onEditorBlur() {}, // 失去焦点事件
    // 获得焦点事件
    onEditorFocus(e) {
      e.enable(!this.disabled); // 禁用文本框
    },
    // 内容改变事件
    onEditorChange(val) {
      if (this.flag) {
        this.flag = false;
        setTimeout(() => {
          this.$refs.myQuillEditor.quill.setSelection(val.html.length + 1);
          this.TiLength = this.TiLength = val.quill.getLength() - 1;
        }, 20);
      }
      this.$emit("editor-content", { value: val.html }); // 触发事件,将富文本框中的值传递出去
      this.contentValue = val.html;
      val.quill.deleteText(4000, 4);
      if (this.contentValue == "") {
        this.TiLength = 0;
      } else {
        this.TiLength = val.quill.getLength() - 1;
      }
    },
    upLoad(e) {
      let files = e.raw;
      if (files.size / 1024 / 1024 > 2) {
        // 定义上传的图片的大小
        Message({
          type: "error",
          message: "请上传小于5M的文件",
        });
        return;
      }
      let fileType = [".jpg", ".png"];
      let suffix = files.name.substring(
        files.name.lastIndexOf("."),
        files.name.lengthquill
      );
      if (fileType.indexOf(suffix) === -1) {
        Message({
          type: "error",
          message: "请上传格式为png、jpg的文件",
        });
        return;
      }
      // 自定义上传路径
      this.handleUpload(e.raw);
    },
    // 上传起牛云写法
    handleUpload(files) {
      this.uploadFile(files);
    },

    // 截屏粘贴到富文本框
    pastePic() {
      // 页面加载之后就监听粘贴事件paste
      this.editor.root.addEventListener(
        "paste",
        (evt) => {
          if (
            evt.clipboardData &&
            evt.clipboardData.files &&
            evt.clipboardData.files.length
          ) {
            evt.preventDefault();
            [].forEach.call(evt.clipboardData.files, (file) => {
              if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
                return;
              }
              this.handleUpload(file);
            });
          }
        },
        false
      );
    },

    //持久化图片
    async sendCdnPicture(e) {
      const { response } = await sendCdnPicture(e);
      this.picUrl = response.urlimage;
      message.success("上传成功", 4);
      let length = this.editor.getSelection().index;
      // 插入图片  res.info为服务器返回的图片地址
      this.editor.insertEmbed(length, "image", this.picUrl);
      // 调整光标到最后
      this.editor.setSelection(length + 1);
    },

    async uploadFile(file) {
      //   实例化七牛云的上传实例

      const uploadClient = new CDNClient({
        env: "test",
        projectName: "xxx_ai_xxxx",
      });
      //创建上传对象
      const uploader = await uploadClient.create(file, {
        authToken: this.uptoken,
        filename: Date.now().toString() + ".jpg",
        onprogress: (ev) => {
          // console.log("event is", ev);
        },
        onsuccess: (res) => {
          this.sendCdnPicture({
            picturename: res.filename.toString(),
            urlimage: res.url.toString(),
          });
        },
      });
      // 上传开始
      uploader.start();
    },

    async getToken() {
      // 获取身份的token
      const { response } = await getCdnToken();
      if (response.status == 1000) {
        this.uptoken = response.results.token;
      }
    },
  },
};
</script>
<style lang="scss">
.editor {
  position: relative;
  .ql-toolbar {
    border: 1px solid #cdcfd4;
    height: 40px;
    background: #f0f2f5;
  }
  .ql-editor {
    min-height: 240px;
    padding: 0;
    padding-top: 15px;
    box-sizing: border-box;
  }
  .ql-container {
    img {
      display: block;
    }
  }
  .wordNumber {
    position: absolute;
    right: 13px;
    bottom: 15px;
    color: #666666;
    font-size: 14px;
  }
}
</style>

 

posted @ 2024-05-08 14:45  凯宾斯基  阅读(9)  评论(0编辑  收藏  举报