fix: 协议设置
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled

This commit is contained in:
2025-05-17 14:15:07 +08:00
parent 9ab3903952
commit a3a09d967e
8 changed files with 696 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
<script setup lang="ts">
import { ref } from 'vue';
import { QuillEditor } from '@vueup/vue-quill';
// eslint-disable-next-line n/no-extraneous-import
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { useVModel } from '@vueuse/core';
import { message } from 'ant-design-vue';
import { uploadFile } from '#/api/core/upload';
const props = defineProps({
value: {
type: String,
default: '',
},
});
const emits = defineEmits(['update:value']);
// 创建对QuillEditor的引用用于后续获取Quill实例
const quillEditorRef = ref(null);
const isUpload = ref(false);
const mValue = useVModel(props, 'value', emits, {
defaultValue: props.value,
passive: true,
});
// 富文本编辑器配置选项
const editorOptions = {
theme: 'snow',
modules: {
toolbar: [
['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'],
],
},
placeholder: '请输入消息内容',
};
/**
* 直接处理编辑器的粘贴事件
* 这是一个备用方法通过Quill的clipboard模块直接处理粘贴事件
*/
const setupQuillPasteHandler = () => {
// 确保编辑器已经挂载
if (!quillEditorRef.value) return;
const quill = quillEditorRef.value.getQuill();
if (!quill) return;
// 获取Quill的clipboard模块
const clipboard = quill.getModule('clipboard');
// 保存原始的粘贴处理函数
const originalMatchers = clipboard.matchers;
// 重写粘贴处理函数
clipboard.addMatcher('img', (node: any, delta: any) => {
// 这里可以处理HTML中的img标签
// 但对于直接粘贴的图片文件,这个方法不会被触发
return delta;
});
// 监听编辑器的paste事件
quill.root.addEventListener('paste', async (e: ClipboardEvent) => {
const imageFile = getImageFromClipboard(e);
if (imageFile) {
e.preventDefault();
if (isUpload.value === true) {
return;
}
isUpload.value = true;
const imageUrl = await uploadImage(imageFile);
if (imageUrl) {
insertImageToEditor(quill, imageUrl);
}
}
});
};
/**
* 从剪贴板数据中提取图片文件
* @param {ClipboardEvent} event - 剪贴板事件
* @returns {File|null} - 返回图片文件或null
*/
const getImageFromClipboard = (event: ClipboardEvent): File | null => {
const clipboardData = event.clipboardData;
if (!clipboardData) return null;
// 遍历剪贴板中的所有项目
const items = clipboardData.items;
for (const item of items) {
// 检查是否是图片类型
if (item.type.includes('image')) {
return item.getAsFile();
}
}
return null;
};
/**
* 在编辑器中插入图片
* @param {any} quill - Quill编辑器实例
* @param {string} imageUrl - 图片URL
*/
const insertImageToEditor = (quill: any, imageUrl: string): void => {
if (!quill) return;
// 获取当前光标位置
const range = quill.getSelection();
if (range) {
// 在光标位置插入图片
quill.insertEmbed(range.index, 'image', imageUrl);
// 将光标移动到图片后面
quill.setSelection(range.index + 1);
} else {
// 如果没有选择范围,则在文档末尾插入
const length = quill.getLength();
quill.insertEmbed(length - 1, 'image', imageUrl);
quill.setSelection(length);
}
};
/**
* 图片上传函数
* @param {File} file - 要上传的图片文件
* @returns {Promise<string>} - 返回上传后的图片URL
*/
const uploadImage = async (file: File): Promise<string> => {
try {
// 创建FormData对象用于发送文件数据
const formData = new FormData();
formData.append('file', file);
// 显示上传中的提示
message.loading({ content: '图片上传中...', key: 'imageUpload' });
return uploadFile({
file,
}).then((data: any) => {
message.success({
content: '图片上传成功',
key: 'imageUpload',
duration: 2,
});
isUpload.value = false;
return data.url;
});
} catch (error) {
// 处理上传错误
console.error('图片上传错误:', error);
message.error({ content: '图片上传失败', key: 'imageUpload', duration: 2 });
return '';
}
};
</script>
<template>
<div class="editor-container-box">
<QuillEditor
ref="quillEditorRef"
v-model:content="mValue"
:options="editorOptions"
class="editor-container"
content-type="html"
theme="snow"
@ready="setupQuillPasteHandler"
/>
</div>
</template>
<style scoped>
.editor-container-box {
width: 100%;
height: 100%;
}
/* 编辑器容器样式 */
.editor-container {
height: 250px;
margin-bottom: 40px;
/* 确保编辑器有足够的空间显示工具栏和内容区域 */
}
/* Markdown编辑器样式 */
.md-editor-container {
min-height: 400px;
margin-bottom: 40px;
}
/* 可以添加额外的样式来自定义编辑器外观 */
:deep(.ql-editor) {
min-height: 200px;
font-size: 14px;
line-height: 1.6;
}
/* 确保图片在编辑器中显示正常 */
:deep(.ql-editor img) {
max-width: 100%;
height: auto;
}
/* 自定义 Markdown 编辑器样式 */
:deep(.md-editor) {
border-radius: 4px;
}
:deep(.md-editor-dark) {
--md-bk-color: #1e1e1e;
--md-border-color: #333;
}
</style>