feat(编辑器): 增强编辑器功能并优化图片处理

- 添加对粘贴图片的支持,自动触发上传流程
- 优化图片插入逻辑,保留原始尺寸信息并改进显示效果
- 重构消息内容解析逻辑,完善数据结构
- 移除冗余的文件插入功能,专注于图片处理优化
- 调整编辑器样式,改进图片显示效果
This commit is contained in:
Phoenix 2025-06-06 10:44:17 +08:00
parent c89056d7f1
commit f279248a51
2 changed files with 143 additions and 61 deletions

View File

@ -240,6 +240,22 @@ const insertMention = (member) => {
const handlePaste = (event) => { const handlePaste = (event) => {
event.preventDefault() event.preventDefault()
//
const items = event.clipboardData?.items
if (items) {
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
//
const file = items[i].getAsFile()
if (file) {
// 使
onUploadFile([file])
return
}
}
}
}
// //
const text = event.clipboardData?.getData('text/plain') || '' const text = event.clipboardData?.getData('text/plain') || ''
@ -295,10 +311,19 @@ const handleKeydown = (event) => {
console.log('editorContent.value', editorContent.value) console.log('editorContent.value', editorContent.value)
console.log('editorHtml.value', editorHtml.value) console.log('editorHtml.value', editorHtml.value)
// //
if (editorContent.value.trim() || // handleInput editorContent.value
editorHtml.value.includes('<img') || if (editorContent.value.trim()) {
editorHtml.value.includes('editor-file') || //
editorHtml.value.includes('editor-emoji')) { const messageData = parseEditorContent()
console.log('编辑器内容解析结果:', JSON.stringify(messageData, null, 2))
//
console.log('消息项目数量:', messageData.items.length)
console.log('消息项目类型:', messageData.items.map(item => item.type))
console.log('提及用户IDs:', messageData.mentionUids)
console.log('引用消息ID:', messageData.quoteId)
//
sendMessage() sendMessage()
} }
} }
@ -309,16 +334,26 @@ const handleKeydown = (event) => {
// //
const sendMessage = () => { const sendMessage = () => {
console.log('发送消息'); console.log('发送消息');
// //
if (!editorContent.value.trim() && if (!editorContent.value.trim()) {
!editorHtml.value.includes('<img') &&
!editorHtml.value.includes('editor-file') &&
!editorHtml.value.includes('editor-emoji')) {
return return
} }
console.log('发送消息1'); console.log('发送消息1');
const messageData = parseEditorContent() const messageData = parseEditorContent()
//
console.log('完整消息数据:', {
items: messageData.items,
mentionUids: messageData.mentionUids,
quoteId: messageData.quoteId,
quoteData: quoteData.value ? {
msg_id: quoteData.value.msg_id,
title: quoteData.value.title,
describe: quoteData.value.describe,
image: quoteData.value.image
} : null
})
if (editingMessage.value) { if (editingMessage.value) {
// //
emit('editor-event', { emit('editor-event', {
@ -361,6 +396,18 @@ const parseEditorContent = () => {
const tempDiv = document.createElement('div') const tempDiv = document.createElement('div')
tempDiv.innerHTML = editorHtml.value tempDiv.innerHTML = editorHtml.value
//
const quoteElements = tempDiv.querySelectorAll('.editor-quote')
let quoteInfo = null
if (quoteElements.length > 0 && quoteData.value) {
quoteInfo = {
msg_id: quoteData.value.msg_id,
title: quoteData.value.title,
describe: quoteData.value.describe,
image: quoteData.value.image
}
}
let textContent = '' let textContent = ''
const nodes = Array.from(tempDiv.childNodes) const nodes = Array.from(tempDiv.childNodes)
@ -368,6 +415,11 @@ const parseEditorContent = () => {
if (node.nodeType === Node.TEXT_NODE) { if (node.nodeType === Node.TEXT_NODE) {
textContent += node.textContent textContent += node.textContent
} else if (node.nodeType === Node.ELEMENT_NODE) { } else if (node.nodeType === Node.ELEMENT_NODE) {
//
if (node.classList.contains('editor-quote')) {
return
}
if (node.classList.contains('mention')) { if (node.classList.contains('mention')) {
const userId = node.getAttribute('data-user-id') const userId = node.getAttribute('data-user-id')
if (userId) { if (userId) {
@ -377,8 +429,8 @@ const parseEditorContent = () => {
} else if (node.tagName === 'IMG') { } else if (node.tagName === 'IMG') {
// //
const src = node.getAttribute('src') const src = node.getAttribute('src')
const width = node.getAttribute('width') || '' const width = node.getAttribute('data-original-width') || node.getAttribute('width') || ''
const height = node.getAttribute('height') || '' const height = node.getAttribute('data-original-height') || node.getAttribute('height') || ''
const isEmoji = node.classList.contains('editor-emoji') const isEmoji = node.classList.contains('editor-emoji')
if (textContent.trim()) { if (textContent.trim()) {
@ -407,7 +459,9 @@ const parseEditorContent = () => {
// //
items.push({ items.push({
type: 3, type: 3,
content: src + (width && height ? `?width=${width}&height=${height}` : '') content: src + (width && height ? `?width=${width}&height=${height}` : ''),
width: width,
height: height
}) })
} }
} else if (node.classList.contains('emoji')) { } else if (node.classList.contains('emoji')) {
@ -447,11 +501,19 @@ const parseEditorContent = () => {
}) })
} }
return { //
const result = {
items: items.length > 0 ? items : [{ type: 1, content: '' }], items: items.length > 0 ? items : [{ type: 1, content: '' }],
mentionUids, mentionUids,
quoteId: quoteData.value?.msg_id || 0 quoteId: quoteData.value?.msg_id || 0
} }
//
if (quoteInfo) {
result.quoteInfo = quoteInfo
}
return result
} }
// //
@ -479,17 +541,24 @@ const clearEditor = () => {
// //
const insertImage = (url, width, height) => { const insertImage = (src, width, height) => {
const selection = window.getSelection() const selection = window.getSelection()
if (!selection.rangeCount) return if (!selection.rangeCount) return
const range = selection.getRangeAt(0) const range = selection.getRangeAt(0)
//
const img = document.createElement('img') const img = document.createElement('img')
img.src = url img.src = src
img.style.maxWidth = '200px' img.className = 'editor-image'
img.alt = '图片'
img.style.maxHeight = '200px' img.style.maxHeight = '200px'
if (width) img.setAttribute('width', width) img.style.maxWidth = '100%'
if (height) img.setAttribute('height', height) img.style.objectFit = 'contain' //
//
if (width) img.setAttribute('data-original-width', width)
if (height) img.setAttribute('data-original-height', height)
range.deleteContents() range.deleteContents()
range.insertNode(img) range.insertNode(img)
@ -502,35 +571,6 @@ const insertImage = (url, width, height) => {
handleInput({ target: editorRef.value }) handleInput({ target: editorRef.value })
} }
//
const insertFile = (url, fileName, fileSize) => {
const selection = window.getSelection()
if (!selection.rangeCount) return
const range = selection.getRangeAt(0)
//
const fileLink = document.createElement('a')
fileLink.href = url
fileLink.target = '_blank'
fileLink.className = 'editor-file'
fileLink.textContent = fileName
fileLink.setAttribute('data-size', formatFileSize(fileSize))
fileLink.setAttribute('data-url', url) // URL
fileLink.setAttribute('data-name', fileName) //
fileLink.setAttribute('data-size-raw', fileSize) //
range.deleteContents()
range.insertNode(fileLink)
range.setStartAfter(fileLink)
range.collapse(true)
selection.removeAllRanges()
selection.addRange(range)
editorRef.value.focus()
handleInput({ target: editorRef.value })
}
// //
const formatFileSize = (size) => { const formatFileSize = (size) => {
if (size < 1024) { if (size < 1024) {
@ -547,21 +587,61 @@ const formatFileSize = (size) => {
/** /**
* 文件上传处理 * 文件上传处理
* @param e 上传事件对象 * @param e 上传事件对象或文件数组
*/ */
async function onUploadFile(e) { //
let file = e.target.files[0] async function onUploadFile(e) {
let files;
e.target.value = null // input //
if (Array.isArray(e)) {
//
files = e;
} else {
//
files = e.target.files;
e.target.value = null; // input
}
//
if (!files || files.length === 0) return;
//
const file = files[0];
console.log("文件类型"+file.type) console.log("文件类型"+file.type)
if (file.type.indexOf('image/') === 0) { if (file.type.indexOf('image/') === 0) {
console.log("进入图片") console.log("进入图片")
// - // URL
let fn = emitCall('image_event', file, () => {}) const tempUrl = URL.createObjectURL(file);
emit('editor-event', fn)
return //
const image = new Image();
image.src = tempUrl;
image.onload = () => {
//
const form = new FormData();
form.append('file', file);
form.append("source", "fonchain-chat"); //
form.append("urlParam", `width=${image.width}&height=${image.height}`);
//
insertImage(tempUrl, image.width, image.height);
// URL
uploadImg(form).then(({ code, data, message }) => {
if (code == 0) {
// URLURL
// URL使
console.log('图片上传成功:', data.ori_url);
} else {
window['$message'].error(message);
}
});
};
return;
} }
if (file.type.indexOf('video/') === 0) { if (file.type.indexOf('video/') === 0) {
@ -1536,12 +1616,14 @@ const onVoteSubmit = (data) => {
* 添加圆角和鼠标指针样式 * 添加圆角和鼠标指针样式
*/ */
.editor-image { .editor-image {
max-width: 100px; max-width: 300px;
max-height: 100px; max-height: 200px;
border-radius: 3px; border-radius: 3px;
background-color: #48484d; background-color: #48484d;
margin: 0px 2px; margin: 0px 2px;
cursor: pointer; cursor: pointer;
object-fit: contain; /* 保持原始比例 */
display: inline-block; /* 确保图片正确显示 */
} }
/** /**

View File

@ -18,7 +18,7 @@ export function isLoggedIn() {
*/ */
export function getAccessToken() { export function getAccessToken() {
// return storage.get(AccessToken) || '' // return storage.get(AccessToken) || ''
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22726726265e9af0db370a54ea5ee002b43662d571b84c8468ac15330f79503a5cd5e72282d8bee92749b1a3c1b7fd87ae70b64b90e437e84c1b558c64a35e181b2ecf5db3007680c3607eac1edee7f59d' return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22403363066ad3c046839f7b2cf8a6186da017388f197c0c3b219b1c04e7d986e9774b72664a22a6075cee77da3584b7a2131365913796a5fcabc8f4594284e480a592a84a40a9aa7f5f27c951a53a369c'
} }
/** /**