富文本编辑器图片变成base6问题
当在另外一个地方**图片,然后回到富文本编辑区里ctrl+v粘贴图片就会使图片变成base64
<template> <div> <!-- 隐藏的图片上传组件 --> <el-upload ref="imageUploader" class="hidden-uploader" :headers="upload.headers" :before-upload="beforeUpload" :http-request="customUpload" :on-success="handleUploadSuccess" :on-error="handleUploadError" :file-list="fileList" :on-exceed="handleExceed" list-type="picture-card" :multiple="true" > <el-button type="primary" style="display: none">点击上传</el-button> </el-upload> <!-- 富文本编辑器 --> <div class="editor"> <quill-editor ref="quillEditorRef" v-model:content="content" content-type="html" :options="options" :style="styles" @text-change="(e) => $emit('update:modelValue', content)" /> </div> </div> </template> <script setup lang="ts"> import { ref, computed, watch, toRaw, onMounted } from 'vue'; import { QuillEditor, Quill } from '@vueup/vue-quill'; import { ElMessage } from 'element-plus'; import '@vueup/vue-quill/dist/vue-quill.snow.css'; import { globalHeaders } from '@/utils/request'; // 假设你有全局 headers 的工具函数 const emit = defineEmits(['update:modelValue']); const props = defineProps({ modelValue: String, height: { type: Number, default: 100 }, minHeight: { type: Number, default: 400 }, readOnly: { type: Boolean, default: false }, fileSize: { type: Number, default: 5 } }); const { proxy } = getCurrentInstance() as ComponentInternalInstance; const upload = { headers: globalHeaders(), url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/config/xxxxxxxx' // 使用预签名 URL 接口 }; const quillEditorRef = ref(); const imageUploader = ref(null); // 引用隐藏的上传组件 const fileList = ref([]); // 监听粘贴事件(自动上传图片) const setupPasteHandler = () => { const editor = quillEditorRef.value.getQuill().root; editor.addEventListener('paste', async (e) => { const items = (e.clipboardData || window.clipboardData).items; let hasImage = false; for (const item of items) { if (item.type.startsWith('image/')) { e.preventDefault(); hasImage = true; const file = item.getAsFile(); if (file) { try { // 插入临时占位 const quill = quillEditorRef.value.getQuill(); const range = quill.getSelection(); const placeholderIndex = range?.index || quill.getLength(); quill.insertEmbed(placeholderIndex, 'image', '[上传中...]'); quill.setSelection(placeholderIndex + 1); console.log("mxmconaaaaaa:",file) // 上传到 OSS const result:any = await customUpload(file); if (result.url) { // 替换占位为真实 URL quill.deleteText(placeholderIndex, 1); quill.insertEmbed(placeholderIndex, 'image', result.url); } else { throw new Error('上传失败'); } } catch (error) { ElMessage.error('图片上传失败: ' + error.message); removeUploadPlaceholder(); } } break; } } // 非图片内容允许正常粘贴 if (!hasImage) { setTimeout(() => { const delta = quillEditorRef.value.getQuill().getContents(); emit('update:modelValue', quillEditorRef.value.getHTML()); }, 0); } }); }; // 移除上传失败的占位 const removeUploadPlaceholder = () => { const quill = quillEditorRef.value.getQuill(); const contents = quill.getContents(); const ops = contents.ops; for (let i = 0; i < ops.length; i++) { if (ops[i].insert?.image === '[上传中...]') { quill.deleteText(i, 1); break; } } }; // 上传队列实现 const uploadQueue = { tasks: [], isProcessing: false, addTask(task) { return new Promise((resolve, reject) => { this.tasks.push({ task, resolve, reject }); this.processNext(); }); }, async processNext() { if (this.isProcessing || this.tasks.length === 0) return; this.isProcessing = true; const { task, resolve, reject } = this.tasks.shift(); try { const result = await task(); resolve(result); } catch (error) { reject(error); } finally { this.isProcessing = false; this.processNext(); } } }; const options = ref({ theme: 'snow', bounds: document.body, debug: 'warn', modules: { toolbar: { container: [ ['bold', 'italic', 'underline', 'strike'], ['blockquote'], [{ list: 'ordered' }, { list: 'bullet' }], [{ indent: '-1' }, { indent: '+1' }], [{ size: [false, 'large'] }], [{ color: [] }, { background: [] }], [{ align: [] }], ['image', 'video'] ], handlers: { image: (value: boolean) => { if (value) { // 触发隐藏的图片上传组件 imageUploader.value.$el.querySelector('input[type="file"]').click(); } else { Quill.format('image', false); } }, video: (value: boolean) => { if (value) { uploadVideo(); } else { Quill.format('video', false); } } } } }, placeholder: '请输入内容', readOnly: props.readOnly }); const styles = computed(() => { let style: any = { width: '100%' }; if (props.minHeight) { style.minHeight = `${props.minHeight}px`; } if (props.height) { style.height = `${props.height}px`; } return style; }); const content = ref(''); watch( () => props.modelValue, (v: string) => { if (v !== content.value) { content.value = v || '<p></p>'; } }, { immediate: true } ); // 图片上传成功回调 const handleUploadSuccess = (response, file, fileList) => { console.log(JSON.stringify(response) + '---->'); if (response.code === 200 || response.url) { const quill = toRaw(quillEditorRef.value).getQuill(); const length = quill.selection.savedRange.index; quill.insertEmbed(length, 'image', response.cdnUrl || response.url); // 插入图片 quill.setSelection(length + 1); ElMessage.success('图片上传成功'); } else { ElMessage.error('图片上传失败:' + response.msg); } }; // 图片上传前校验 const beforeUpload = (file) => { const isImage = file.type.startsWith('image/'); const isLt5M = file.size / 1024 / 1024 < props.fileSize; if (!isImage) { ElMessage.error('只能上传图片格式!'); return false; } if (!isLt5M) { ElMessage.error(`上传文件大小不能超过 ${props.fileSize} MB!`); return false; } return true; }; // 自定义图片上传逻辑 const customUpload = async (file) => { file = file.file || file; if (!file) return; return uploadQueue.addTask(async () => { const formData = { originalFileName: file.name }; try { const response = await fetch(upload.url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.code === 200) { const signedUrl = result.data.preSigneUrl; const uploadResponse = await fetch(signedUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } }); if (uploadResponse.ok) { return { url: result.data.cdnUrl, storageId: result.data.objectName }; } else { ElMessage.error(`图片上传失败:${uploadResponse.statusText}`); throw new Error('Upload failed'); } } else { ElMessage.error(`获取预签名 URL 失败:${result.msg}`); throw new Error('Get presigned URL failed'); } } catch (error) { ElMessage.error(`上传过程中出现错误:${error.message}`); throw error; } }); }; // 图片上传超出限制回调 const handleExceed = (files: File[]) => { ElMessage.error('最多只能上传20张图片'); }; // 图片上传失败回调 const handleUploadError = () => { ElMessage.error('图片上传失败,请稍后再试!'); }; // 视频上传逻辑 const uploadVideo = async () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'video/*'; input.onchange = async (e: any) => { const file = e.target.files[0]; if (!file) return; const isVideo = file.type.startsWith('video/'); const isLt50M = file.size / 1024 / 1024 < 50; if (!isVideo) { ElMessage.error('只能上传视频格式!'); return; } if (!isLt50M) { ElMessage.error('上传文件大小不能超过 50MB!'); return; } proxy?.$modal.loading('正在上传视频,请稍候...'); const formData = { originalFileName: file.name }; try { const response = await fetch(upload.url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.code === 200) { const signedUrl = result.data.preSigneUrl; const uploadResponse = await fetch(signedUrl, { method: 'PUT', body: file, headers: { 'Content-Type': file.type } }); if (uploadResponse.ok) { const quill = toRaw(quillEditorRef.value).getQuill(); const length = quill.selection.savedRange.index; quill.insertEmbed(length, 'video', result.data.cdnUrl); // 插入视频 quill.setSelection(length + 1); proxy?.$modal.closeLoading(); } else { ElMessage.error('视频上传失败:' + uploadResponse.statusText); proxy?.$modal.closeLoading(); } } else { ElMessage.error('获取预签名 URL 失败:' + result.msg); proxy?.$modal.closeLoading(); } } catch (error) { ElMessage.error('上传过程中出现错误:' + error.message); proxy?.$modal.closeLoading(); } }; input.click(); }; onMounted(() => { setupPasteHandler(); }); </script> <style scoped> .editor, .ql-toolbar { width: 100%; white-space: pre-wrap; line-height: normal; } .hidden-uploader { display: none; /* 隐藏上传组件 */ } .ql-video { max-width: 100%; height: auto; display: block; margin: 10px 0; } ::v-deep .ql-snow .ql-editor img{ display: block; max-width: 200px; margin: 0 auto; } </style>
您还未登录, 登录 后可进行评论
发表
还没有评论哦,来抢个沙发吧!