fix(editor): 修复编辑器空内容判断和换行处理问题
改进编辑器空内容检测逻辑,确保更准确地判断是否为空内容 重构换行处理逻辑,使用辅助函数插入换行符并保持光标位置 优化消息发送前的空内容检查,防止发送无效消息
This commit is contained in:
parent
d46ced7614
commit
88bbf16699
@ -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 !== '<br>') {
|
||||
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 <br>
|
||||
// and that the <br> 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(/<br\s*\/?>/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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -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'
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user