使用 @vueup/vue-quill 想要注册一个模块,发现按照搜索出来的写法没有效果
Quill.register('modules/imageResize', ImageResize);
自己手动写了个ImageResize的类,发现constructor方法都没有被触发。
后面还是去仔细看了官方文档
<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; }}