在vue中使用quill-editor富文本编辑器(自定义工具栏、重写上传文件功能、工具栏中增加额外的功能按钮)

下面对quill-editor的使用包含了自定义工具栏、重写工具栏中的上传文件功能、在工具栏中新增额外的功能按钮

进入我们的项目文件夹,并打开命令行窗口,然后进行下面的步骤:
1、安装vue-quill-editor
输入命令:npm install vue-quill-editor --save
2、引入插件
在项目的入口文件main.js中引入插件,如下:

import VueQuillEditor from 'vue-quill-editor' // 调用编辑器
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
Vue.use(VueQuillEditor)
require('video.js/dist/video-js.css')
require('vue-video-player/src/custom-theme.css')

3、使用插件
创建vue组件文件QuillEditor.vue,文件内容如下:

<template>
  <div>
    <quill-editor
      v-model="editorContent"
      ref="quillEditor"
      :options="editorOption"
    ></quill-editor>
    <!--重写富文本编辑器中的上传图片功能 bg-->
    <el-upload style="display:none"
               :action="uploadImgUrl"
               name="imgFile"
               :show-file-list="false"
               :before-upload='quillBeforeUploadFunc'
               :on-success='quillUploadSuccess'
               ref="quill-upload-img"
               :id="'quill-upload-img-' + index"
    >
    </el-upload>
    <!--重写富文本编辑器中的上传图片功能 end-->
  </div>
</template>

<script>
import Axios from 'axios'
import {
    
     quillEditor } from 'vue-quill-editor' // 调用编辑器

var Quill = require('quill/dist/quill.js')
const IMAGE_TYPES = ['jpg', 'png', 'tif', 'gif', 'jpeg', 'icon']
<think>根据用户需求,我们需要在Vue3+Element Plus+@vueup/vue-quill的环境中实现工具栏图片上传功能。关键点在于重写Quill编辑器的图片处理函数,使用Element Plus的Upload组件进行图片上传,并将返回的图片URL插入编辑器。 参考引用[1][2][3]的思路,我们可以这样实现: 1. 隐藏Element Plus的上传组件(el-upload) 2. 在Quill工具栏配置中重写图片处理函数(image handler) 3. 在自定义处理函数中触发el-upload的点击事件 4. 上传成功后获取图片URL并插入编辑器光标位置 具体实现步骤: 1. 安装依赖 ```bash npm install @vueup/vue-quill quill@^1.3.7 ``` 2. 创建富文本编辑器组件(QuillEditor.vue) ```vue <template> <div class="editor-container"> <!-- 隐藏的上传组件 --> <el-upload ref="uploader" action="/api/upload" // 替换为你的上传接口 :show-file-list="false" :on-success="handleImageSuccess" :before-upload="beforeImageUpload" style="display: none" > </el-upload> <!-- Quill富文本编辑器 --> <quill-editor ref="editor" v-model:content="content" :options="editorOptions" contentType="html" /> </div> </template> <script setup> import { ref } from 'vue'; import { QuillEditor } from '@vueup/vue-quill'; import { ElMessage } from 'element-plus'; const content = ref(''); const uploader = ref(null); // 上传组件引用 // 编辑器配置 const editorOptions = ref({ modules: { toolbar: { container: [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{ 'header': 1 }, { 'header': 2 }], [{ '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'] // 包含图片按钮 ], handlers: { // 重写图片处理函数 image: () => { // 触发上传组件的点击事件 uploader.value.$el.querySelector('input[type=file]').click(); } } } } }); // 图片上传前校验 const beforeImageUpload = (file) => { const isImage = file.type.startsWith('image/'); const isLt5M = file.size / 1024 / 1024 < 5; if (!isImage) { ElMessage.error('只能上传图片文件!'); } if (!isLt5M) { ElMessage.error('图片大小不能超过5MB!'); } return isImage && isLt5M; }; // 图片上传成功处理 const handleImageSuccess = (response, file) => { // 假设返回数据格式为 { url: 'https://example.com/image.jpg' } const imageUrl = response.url; // 获取编辑器实例 const editor = editorRef.value.getQuill(); // 获取当前光标位置 const range = editor.getSelection(); // 在光标位置插入图片 editor.insertEmbed(range.index, 'image', imageUrl); // 将光标移动到图片后面 editor.setSelection(range.index + 1); }; </script> <style> @import '~@vueup/vue-quill/dist/vue-quill.snow.css'; </style> ``` 3. 在父组件中使用 ```vue <template> <div> <quill-editor /> </div> </template> <script setup> import QuillEditor from './QuillEditor.vue'; </script> ``` ### 关键点说明 1. **重写图片处理函数**: - 通过`handlers`覆盖默认的图片处理函数 - 在函数中触发隐藏的`el-upload`组件 2. **上传流程控制**: - 使用Element Plus的`before-upload`进行文件校验 - 上传成功后通过`insertEmbed`方法插入图片URL 3. **光标位置处理**: - 获取当前编辑器光标位置`editor.getSelection()` - 在正确位置插入图片后移动光标 ### 注意事项 1. **上传接口适配**: ```javascript // 如果接口返回格式不同,需要调整 const imageUrl = response.data.url; // 根据实际返回结构调整 ``` 2. **跨域问题**: - 如果前端地址与上传接口不同源,需配置代理 ```javascript // vite.config.js export default defineConfig({ server: { proxy: { '/api': { target: 'http://your-upload-server.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } }) ``` 3. **错误处理**: ```javascript // 添加上传失败处理 :on-error="handleImageError" const handleImageError = (error) => { ElMessage.error(`图片上传失败: ${error.message}`); }; ``` 4. **多图上传支持**: - 如需多图上传,设置`multiple`属性 ```html <el-upload multiple></el-upload> ``` - 在成功回调中遍历插入所有图片 ```javascript response.urls.forEach(url => { editor.insertEmbed(range.index, 'image', url); range.index++; }); ``` ### 相关问题 1. **如何实现拖拽上传图片?** 监听编辑器的`drop`事件: ```javascript editor.container.addEventListener('drop', (e) => { e.preventDefault(); if (e.dataTransfer.files.length) { // 触发上传 } }); ``` 2. **如何粘贴图片自动上传?** 监听`paste`事件: ```javascript editor.container.addEventListener('paste', (e) => { const items = e.clipboardData.items; // 遍历items找到图片 }); ``` 3. **如何限制图片大小?** 在`beforeImageUpload`中校验: ```javascript const isLt5M = file.size / 1024 / 1024 < 5; ``` 4. **如何实现图片压缩?** 使用`canvas`进行前端压缩: ```javascript const compressImage = (file) => { // 压缩逻辑 return compressedFile; }; ``` 5. **如何解决中文语言问题?** 引入中文语言包: ```javascript import { Quill } from '@vueup/vue-quill'; import zh from 'quill/lang/zh-cn'; Quill.register('lang/zh-cn', zh); ``` 然后在编辑器配置中添加: ```javascript editorOptions: { locale: 'zh-cn' } ``` [^1]: vue-quill-editor富文本编辑器自定义上传图片功能 [^2]: vue3中使用vue-quill富文本编辑器 重写图片上传功能 [^3]: vue整合quill-editor汉化+element文件上传</think># Vue3 + Element Plus + Vue-Quill 富文本编辑器图片上传实现 我将为您展示如何在 Vue3 + Element Plus + Vue-Quill富文本编辑器中实现工具栏点击上传图片的功能,避免使用 base64 格式存储图片。 ## 实现思路 1. 使用 Vue-Quill 作为富文本编辑器 2. 重写工具栏的图片处理函数 3. 使用 Element Plus 的 Upload 组件实现图片上传 4. 上传成功后插入图片 URL 到编辑器 ## 完整实现代码 ```vue <template> <div class="editor-container"> <!-- 富文本编辑器 --> <quill-editor ref="quillEditor" v-model:content="content" :options="editorOptions" contentType="html" class="quill-editor" /> <!-- 隐藏的上传组件 --> <el-upload ref="imageUploader" action="/api/upload" :show-file-list="false" :auto-upload="true" :on-success="handleImageSuccess" :on-error="handleImageError" :before-upload="beforeImageUpload" style="display: none" > </el-upload> </div> </template> <script setup> import { ref } from 'vue'; import { QuillEditor } from '@vueup/vue-quill'; import { ElMessage } from 'element-plus'; // 编辑器内容 const content = ref(''); // 获取编辑器实例和上传组件引用 const quillEditor = ref(null); const imageUploader = ref(null); // 编辑器配置 const editorOptions = ref({ placeholder: '请输入内容...', theme: 'snow', modules: { toolbar: { container: [ ['bold', 'italic', 'underline', 'strike'], ['blockquote', 'code-block'], [{ 'header': 1 }, { 'header': 2 }], [{ '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'] ], // 重写图片处理函数 handlers: { image: () => { // 触发上传组件的点击事件 imageUploader.value.$el.querySelector('input[type=file]').click(); } } } } }); // 图片上传前校验 const beforeImageUpload = (file) => { const isImage = file.type.startsWith('image/'); const isLt5M = file.size / 1024 / 1024 < 5; if (!isImage) { ElMessage.error('只能上传图片文件!'); } if (!isLt5M) { ElMessage.error('图片大小不能超过5MB!'); } return isImage && isLt5M; }; // 图片上传成功处理 const handleImageSuccess = (response, file) => { // 假设返回数据格式为 { url: 'https://example.com/image.jpg' } const imageUrl = response.url; // 获取编辑器实例 const editor = quillEditor.value.getQuill(); // 获取当前光标位置 const range = editor.getSelection(); if (range) { // 在光标位置插入图片 editor.insertEmbed(range.index, 'image', imageUrl); // 将光标移动到图片后面 editor.setSelection(range.index + 1); } else { // 如果没有选中位置,在末尾插入图片 editor.insertEmbed(editor.getLength(), 'image', imageUrl); } ElMessage.success('图片上传成功!'); }; // 图片上传失败处理 const handleImageError = (error, file) => { ElMessage.error(`图片上传失败: ${error.message}`); }; </script> <style scoped> .editor-container { max-width: 1000px; margin: 20px auto; border: 1px solid #dcdfe6; border-radius: 4px; overflow: hidden; } .quill-editor { height: 500px; } :deep(.ql-editor) { min-height: 400px; font-size: 16px; line-height: 1.6; } :deep(.ql-toolbar) { border-top-left-radius: 4px; border-top-right-radius: 4px; background-color: #f8f8f8; border-bottom: 1px solid #dcdfe6; } :deep(.ql-container) { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; border: none !important; } </style> ``` ## 关键实现说明 1. **重写图片处理函数**: -工具栏配置的 `handlers` 中重写了 `image` 方法 - 当点击图片按钮时,触发隐藏的上传组件的点击事件 2. **隐藏的上传组件**: - 使用 Element Plus 的 `<el-upload>` 组件实现图片上传 - 设置 `style="display: none"` 隐藏组件 - 配置了上传前校验、成功和失败处理函数 3. **图片插入逻辑**: - 上传成功后获取图片 URL - 获取编辑器当前光标位置 - 使用 `insertEmbed` 方法在光标位置插入图片 - 移动光标到图片后 4. **用户体验优化**: - 文件类型和大小校验 - 上传成功/失败提示 - 响应式设计 ## 使用示例 在父组件中使用富文本编辑器组件: ```vue <template> <div class="container"> <h1>富文本编辑器演示</h1> <rich-text-editor v-model="content" /> <div class="preview mt-8"> <h2>内容预览:</h2> <div v-html="content" class="preview-content"></div> </div> </div> </template> <script setup> import { ref } from 'vue'; import RichTextEditor from './components/RichTextEditor.vue'; const content = ref(''); </script> <style scoped> .container { max-width: 1200px; margin: 0 auto; padding: 20px; } .preview { border: 1px solid #eee; padding: 20px; border-radius: 4px; margin-top: 30px; } .preview-content { margin-top: 15px; padding: 15px; background-color: #f9f9f9; border-radius: 4px; min-height: 100px; } </style> ``` ## 相关问题 ### 1. 如何实现图片压缩功能? 可以通过在 `beforeImageUpload` 方法中添加图片压缩逻辑: ```javascript const compressImage = (file) => { return new Promise((resolve) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (event) => { const img = new Image(); img.src = event.target.result; img.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置压缩后的最大尺寸 const MAX_WIDTH = 1200; const MAX_HEIGHT = 1200; let width = img.width; let height = img.height; if (width > height) { if (width > MAX_WIDTH) { height *= MAX_WIDTH / width; width = MAX_WIDTH; } } else { if (height > MAX_HEIGHT) { width *= MAX_HEIGHT / height; height = MAX_HEIGHT; } } canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); // 转换为Blob canvas.toBlob((blob) => { resolve(new File([blob], file.name, { type: 'image/jpeg', lastModified: Date.now() })); }, 'image/jpeg', 0.8); }; }; }); }; // 在beforeImageUpload中调用 const beforeImageUpload = async (file) => { // ...校验逻辑 // 压缩图片 const compressedFile = await compressImage(file); return compressedFile; }; ``` ### 2. 如何实现粘贴图片自动上传? 可以监听编辑器的粘贴事件: ```javascript import { onMounted } from 'vue'; onMounted(() => { const editorElement = quillEditor.value.$el.querySelector('.ql-editor'); editorElement.addEventListener('paste', async (event) => { const items = event.clipboardData.items; for (let i = 0; i < items.length; i++) { if (items[i].type.startsWith('image')) { event.preventDefault(); const file = items[i].getAsFile(); if (file) { // 上传图片 const formData = new FormData(); formData.append('image', file); try { const response = await fetch('/api/upload', { method: 'POST', body: formData }); const data = await response.json(); handleImageSuccess(data, file); } catch (error) { handleImageError(error, file); } } } } }); }); ``` ### 3. 如何解决跨域问题? 在开发环境中配置代理(vite.config.js): ```javascript import { defineConfig } from 'vite' export default defineConfig({ server: { proxy: { '/api': { target: 'https://your-image-server.com', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } }) ``` ### 4. 如何实现多图上传? 修改上传组件和成功处理函数: ```html <el-upload ref="imageUploader" action="/api/upload" :multiple="true" :show-file-list="false" :auto-upload="true" :on-success="handleImageSuccess" :before-upload="beforeImageUpload" style="display: none" > </el-upload> ``` ```javascript const handleImageSuccess = (response, file, fileList) => { const editor = quillEditor.value.getQuill(); const range = editor.getSelection() || { index: editor.getLength() }; // 插入所有上传成功的图片 fileList.forEach(file => { if (file.status === 'success') { editor.insertEmbed(range.index, 'image', file.response.url); range.index++; } }); // 移动光标到最后 editor.setSelection(range.index); }; ``` 这个实现方案解决了默认 base64 存储图片的问题,通过自定义上传功能将图片保存到服务器,只存储图片路径,提高了性能和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值