diff --git a/src/components/editor/CustomEditor.vue b/src/components/editor/CustomEditor.vue index afb7e99..7711510 100644 --- a/src/components/editor/CustomEditor.vue +++ b/src/components/editor/CustomEditor.vue @@ -123,11 +123,16 @@ const handleInput = (event) => { - const isEmpty = textContent === '' && - !target.querySelector('img, .editor-file, .mention') - - if (isEmpty && target.innerHTML !== '') { - target.innerHTML = '' + const editorNode = target; + const currentNormalizedHtml = editorNode.innerHTML.trim().toLowerCase().replace(/\s+/g, ''); + + const hasTextContent = editorNode.textContent.trim() !== ''; + const hasSpecialElements = editorNode.querySelector('img, .editor-file, .mention'); + + if (!hasTextContent && !hasSpecialElements) { + if (currentNormalizedHtml !== '' && currentNormalizedHtml !== '
') { + editorNode.innerHTML = ''; + } } @@ -344,6 +349,36 @@ const handlePaste = (event) => { } +// Helper function to insert line break +const insertLineBreak = (range) => { + const editor = editorRef.value; + if (!editor) return; + + const br = document.createElement('br'); + range.deleteContents(); // Clear selected content or collapsed cursor position + range.insertNode(br); + + // Create a zero-width space or a text node to ensure the cursor can be placed after the
+ // and that the
is not immediately removed by cleanup logic if it's the only content. + const nbsp = document.createTextNode('\u200B'); // Zero-width space + range.setStartAfter(br); + range.insertNode(nbsp); + range.setStartAfter(nbsp); + range.collapse(true); + + const selection = window.getSelection(); + if (selection) { + selection.removeAllRanges(); + selection.addRange(range); + } + + // Ensure editor focus and trigger input handling + editor.focus(); + nextTick(() => { + handleInput({ target: editor }); + }); +}; + const handleKeydown = (event) => { if (showMention.value) { @@ -531,45 +566,61 @@ const handleKeydown = (event) => { if (event.key === 'Enter' && (event.ctrlKey || event.metaKey || event.shiftKey)) { - - const selection = window.getSelection() - if (selection && selection.rangeCount > 0) { - const range = selection.getRangeAt(0) - const br = document.createElement('br') - range.deleteContents() - range.insertNode(br) - - - const textNode = document.createTextNode('') - range.setStartAfter(br) - range.insertNode(textNode) - range.setStartAfter(textNode) - range.collapse(true) - selection.removeAllRanges() - selection.addRange(range) - - - handleInput({ target: editorRef.value }) + event.preventDefault(); + + const editor = editorRef.value; + if (!editor) return; + + const selection = window.getSelection(); + if (!selection || selection.rangeCount === 0) { + // 如果没有选区,尝试聚焦并创建选区 + editor.focus(); + // 等待DOM更新 + nextTick(() => { + const newSelection = window.getSelection(); + if (newSelection && newSelection.rangeCount > 0) { + insertLineBreak(newSelection.getRangeAt(0)); + } + }); + return; } - - - event.preventDefault() - return + + insertLineBreak(selection.getRangeAt(0)); + return; } + + + if (event.key === 'Enter' && !event.ctrlKey && !event.metaKey && !event.shiftKey) { - event.preventDefault() - - - const editor = editorRef.value - const quoteElement = editor?.querySelector('.editor-quote') - if (!quoteElement && quoteData.value) { - quoteData.value = null + event.preventDefault(); + + const editor = editorRef.value; + if (!editor) return; + + const messageData = parseEditorContent(); + const isEmptyMessage = messageData.items.length === 0 || + (messageData.items.length === 1 && + messageData.items[0].type === 1 && + !messageData.items[0].content.trimEnd()); + + if (isEmptyMessage) { + // If the message is considered empty, prevent sending. + // Ensure editor is truly empty if it wasn't already. + if (editor.innerHTML !== '') { + clearEditor(); + } + return; } - - - sendMessage() + + // If message is not empty, proceed to send. + const quoteElement = editor.querySelector('.editor-quote'); + if (!quoteElement && quoteData.value) { + quoteData.value = null; + } + + sendMessage(); } } @@ -592,10 +643,15 @@ const sendMessage = () => { } messageData.items.forEach(item => { - if (item.type === 1 && cleanInvisibleChars(item.content.trimEnd())) { + if (item.type === 1 && cleanInvisibleChars(item.content).trimEnd()) { // Apply trimEnd after cleaning + const finalContent = cleanInvisibleChars(item.content).replace(//gi, '\n').trimEnd(); + if (!finalContent && !messageData.mentionUids.length && !messageData.quoteId) { + // If after processing, the content is empty and no mentions/quote, skip + return; + } const data = { items: [{ - content: cleanInvisibleChars(item.content), + content: finalContent, // Use the processed content type: 1 }], mentionUids: messageData.mentionUids, @@ -655,36 +711,30 @@ const parseEditorContent = () => { const processNode = (node) => { if (node.nodeType === Node.TEXT_NODE) { - - textContent += node.textContent - return + textContent += node.textContent; + return; } - - if (node.nodeType !== Node.ELEMENT_NODE) return - - - if (node.classList.contains('mention')) { - - const userId = node.getAttribute('data-user-id') + + if (node.nodeType !== Node.ELEMENT_NODE) return; + + if (node.tagName === 'BR') { + textContent += '\n'; + } else if (node.classList.contains('mention')) { + const userId = node.getAttribute('data-user-id'); if (userId) { - mentionUids.push(Number(userId)) + mentionUids.push(Number(userId)); } - textContent += node.textContent + textContent += node.textContent; } else if (node.tagName === 'IMG') { - - processImage(node) + processImage(node); } else if (node.classList.contains('emoji')) { - - textContent += node.getAttribute('alt') || node.textContent + textContent += node.getAttribute('alt') || node.textContent; } else if (node.classList.contains('editor-file')) { - - processFile(node) + processFile(node); } else if (node.childNodes.length) { - - Array.from(node.childNodes).forEach(processNode) + Array.from(node.childNodes).forEach(processNode); } else { - - textContent += node.textContent + textContent += node.textContent; } } @@ -758,11 +808,11 @@ const parseEditorContent = () => { Array.from(tempDiv.childNodes).forEach(processNode) - if (textContent.trim()) { + if (textContent) { items.push({ type: 1, - content: textContent.trimEnd() - }) + content: textContent + }); } diff --git a/src/utils/auth.js b/src/utils/auth.js index aeaeb1b..7dcbede 100644 --- a/src/utils/auth.js +++ b/src/utils/auth.js @@ -18,7 +18,7 @@ export function isLoggedIn() { */ export function getAccessToken() { // return storage.get(AccessToken) || '' - return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b891a491a664540c3af42964b31bedf8b1c93e8a754bb71e4b95d53ad8e6b16ac1575f536a9e7a062e44f3bb48a367623d38bd875a10afa3a53e79374ffda424138ed9ad4cab0d972432567ae7149b2bf3c' + return JSON.parse(localStorage.getItem('token'))||'79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b8993ebccf0349a53e3197efc45b9dbe3f2bf1dc0dddce6787811964e76efefec3b3fd39fce15d43989c156413f12de3f0c74c1ff1d3c5da214d3bcefef7546498e37fa73453c749a56ea66777488bd3550' } /**