vue-quill注册模块quill-image-resize-module

487 阅读1分钟

使用 @vueup/vue-quill   想要注册一个模块,发现按照搜索出来的写法没有效果

Quill.register('modules/imageResize', ImageResize);

自己手动写了个ImageResize的类,发现constructor方法都没有被触发。

后面还是去仔细看了官方文档

vueup.github.io/vue-quill/g…

<template>
  <QuillEditor :modules="modules" toolbar="full" />
</template>

<script>
import { ref, defineComponent } from 'vue'
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
import ImageUploader from 'quill-image-uploader';
import axios from '../lib/axios';

export default defineComponent({
  components: {
    QuillEditor,
  },
  setup: () => {
    const modules = {
        name: 'imageUploader',
        module: ImageUploader,
        options: {
          upload: file => {
            return new Promise((resolve, reject) => {
              const formData = new FormData();
              formData.append("image", file);

              axios.post('/upload-image', formData)
              .then(res => {
                console.log(res)
                resolve(res.data.url);
              })
              .catch(err => {
                reject("Upload failed");
                console.error("Error:", err)
              })
            })
          }
    return { modules }
  }
})
</script>

是要传入一个modules属性来实现配置

附赠一个image-resize 插件的代码,现有的轮子使用的时候遇到一堆问题,就干脆自己写了。

ts代码:

import type Quill from 'quill';export default class ImageResize {  quill: Quill;  options: any;  currentImage: HTMLElement | null;  overlay: HTMLElement | null;  handles: HTMLElement[];  constructor(quill: any, options: any) {    this.quill = quill;    this.options = options;    this.currentImage = null;    this.overlay = null;    this.handles = [];    this.initializeModule();  }  initializeModule() {    // 创建调整大小的覆盖层    this.createOverlay();    // 监听编辑器中的图片点击    this.quill.root.addEventListener('click', (e: MouseEvent) => {      const image = e.target as HTMLElement;      if (image.tagName === 'IMG') {        this.show(image);      } else if (this.currentImage && !(e.target as HTMLElement).closest('.image-resize-overlay')) {        this.hide();      }    });    // 点击其他地方时隐藏    document.addEventListener('click', (e: MouseEvent) => {      if (        !this.quill.root.contains(e.target as Node) &&        !(e.target as HTMLElement).closest('.image-resize-overlay')      ) {        this.hide();      }    });    // 任何滚动都隐藏编辑框    window.addEventListener(      'scroll',      () => {        this.hide();      },      true    );  }  createOverlay() {    this.overlay = document.createElement('div');    this.overlay.className = 'image-resize-overlay';    // 创建八个调整手柄    const positions = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];    positions.forEach((pos) => {      const handle = document.createElement('div');      handle.className = `resize-handle ${pos}`;      this.handles.push(handle);      this.overlay?.appendChild(handle);      handle.addEventListener('mousedown', this.startResize.bind(this, pos));    });    document.body.appendChild(this.overlay);  }  show(image: HTMLElement) {    if (this.currentImage === image) return;    this.currentImage = image;    this.positionOverlay();    this.overlay!.style.display = 'block';  }  hide() {    if (this.overlay) {      this.overlay.style.display = 'none';    }    this.currentImage = null;  }  positionOverlay() {    if (!this.currentImage || !this.overlay) return;    const rect = this.currentImage.getBoundingClientRect();    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;    const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;    Object.assign(this.overlay.style, {      top: `${rect.top + scrollTop}px`,      left: `${rect.left + scrollLeft}px`,      width: `${rect.width}px`,      height: `${rect.height}px`    });  }  startResize(direction: string, e: MouseEvent) {    e.preventDefault();    if (!this.currentImage) return;    const startX = e.clientX;    const startY = e.clientY;    const startWidth = this.currentImage.offsetWidth;    const startHeight = this.currentImage.offsetHeight;    const onMouseMove = (e: MouseEvent) => {      const deltaX = e.clientX - startX;      const deltaY = e.clientY - startY;      let newWidth = startWidth;      let newHeight = startHeight;      switch (direction) {        case 'e': // 东          newWidth = startWidth + deltaX;          newHeight = (newWidth / startWidth) * startHeight;          break;        case 'se': // 东南          newWidth = startWidth + deltaX;          newHeight = startHeight + deltaY;          break;        case 's': // 南          newHeight = startHeight + deltaY;          newWidth = (newHeight / startHeight) * startWidth;          break;        case 'sw': // 西南          newWidth = startWidth - deltaX;          newHeight = startHeight + deltaY;          break;        case 'w': // 西          newWidth = startWidth - deltaX;          newHeight = (newWidth / startWidth) * startHeight;          break;        case 'nw': // 西北          newWidth = startWidth - deltaX;          newHeight = startHeight - deltaY;          break;        case 'n': // 北          newHeight = startHeight - deltaY;          newWidth = (newHeight / startHeight) * startWidth;          break;        case 'ne': // 东北          newWidth = startWidth + deltaX;          newHeight = startHeight - deltaY;          break;      }      // 设置最小尺寸      newWidth = Math.max(30, newWidth);      newHeight = Math.max(30, newHeight);      this.resize(newWidth, newHeight);    };    const onMouseUp = () => {      document.removeEventListener('mousemove', onMouseMove);      document.removeEventListener('mouseup', onMouseUp);    };    document.addEventListener('mousemove', onMouseMove);    document.addEventListener('mouseup', onMouseUp);  }  resize(width: number, height: number) {    if (!this.currentImage) return;    // 设置图片样式    this.currentImage.style.width = `${width}px`;    this.currentImage.style.height = `${height}px`;    // 更新图片属性    this.currentImage.setAttribute('width', `${width}`);    this.currentImage.setAttribute('height', `${height}`);    // 触发 Quill 内容更新    const range = this.quill.getSelection();    this.quill.setSelection(range);    this.positionOverlay();  }  updateOverlayPosition = () => {    if (this.currentImage && this.overlay) {      // 检查图片是否在视口内      const rect = this.currentImage.getBoundingClientRect();      const isVisible =        rect.top >= 0 &&        rect.left >= 0 &&        rect.bottom <= window.innerHeight &&        rect.right <= window.innerWidth;      if (!isVisible) {        this.hide();        return;      }      this.positionOverlay();    }  };}

样式定义

.image-resize-overlay {  position: absolute;  display: none;  border: 1px solid #0066ff;  pointer-events: none;}.resize-handle {  position: absolute;  width: 8px;  height: 8px;  background-color: #0066ff;  border: 1px solid #fff;  pointer-events: all;  &.nw {    top: -4px;    left: -4px;    cursor: nw-resize;  }  &.n {    top: -4px;    left: 50%;    margin-left: -4px;    cursor: n-resize;  }  &.ne {    top: -4px;    right: -4px;    cursor: ne-resize;  }  &.e {    top: 50%;    right: -4px;    margin-top: -4px;    cursor: e-resize;  }  &.se {    bottom: -4px;    right: -4px;    cursor: se-resize;  }  &.s {    bottom: -4px;    left: 50%;    margin-left: -4px;    cursor: s-resize;  }  &.sw {    bottom: -4px;    left: -4px;    cursor: sw-resize;  }  &.w {    top: 50%;    left: -4px;    margin-top: -4px;    cursor: w-resize;  }}