refactor(editor): 优化编辑器输入处理逻辑和性能
- 重构输入事件处理函数,减少不必要的DOM操作 - 简化键盘事件处理逻辑,移除冗余日志 - 优化消息发送逻辑,增加内容检查 - 改进引用元素处理,增强交互体验 - 统一表情处理逻辑,使用switch语句替代if-else - 优化草稿保存和加载机制,使用DocumentFragment提高性能 - 清理冗余代码和注释,保持代码简洁
This commit is contained in:
parent
3ec981ea7f
commit
7fea56f704
@ -94,7 +94,7 @@ const toolbarConfig = computed(() => {
|
|||||||
return config
|
return config
|
||||||
})
|
})
|
||||||
|
|
||||||
// 处理输入事件
|
// 处理输入事件 - 优化版本,减少DOM操作
|
||||||
const handleInput = (event) => {
|
const handleInput = (event) => {
|
||||||
const target = event.target
|
const target = event.target
|
||||||
|
|
||||||
@ -108,24 +108,33 @@ const handleInput = (event) => {
|
|||||||
let textContent = editorClone.textContent || ''
|
let textContent = editorClone.textContent || ''
|
||||||
|
|
||||||
// 将表情图片的 alt 属性添加到文本内容中
|
// 将表情图片的 alt 属性添加到文本内容中
|
||||||
emojiImages.forEach(emoji => {
|
if (emojiImages.length > 0) {
|
||||||
const altText = emoji.getAttribute('alt')
|
emojiImages.forEach(emoji => {
|
||||||
if (altText) {
|
const altText = emoji.getAttribute('alt')
|
||||||
textContent += altText
|
if (altText) {
|
||||||
}
|
textContent += altText
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 更新编辑器内容(包含表情文本)
|
// 更新逻辑文本内容
|
||||||
editorContent.value = textContent
|
editorContent.value = textContent
|
||||||
|
|
||||||
|
// 检查是否需要清空编辑器以显示placeholder
|
||||||
|
const isEmpty = textContent.trim() === '' &&
|
||||||
|
!target.querySelector('img, .editor-file, .mention')
|
||||||
|
|
||||||
|
if (isEmpty && target.innerHTML !== '') {
|
||||||
|
target.innerHTML = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新HTML内容
|
||||||
editorHtml.value = target.innerHTML || ''
|
editorHtml.value = target.innerHTML || ''
|
||||||
|
|
||||||
// 检查@mention
|
// 后续操作
|
||||||
checkMention(target)
|
checkMention(target)
|
||||||
|
|
||||||
// 保存草稿
|
|
||||||
saveDraft()
|
saveDraft()
|
||||||
|
|
||||||
// 发送输入事件
|
|
||||||
emit('editor-event', {
|
emit('editor-event', {
|
||||||
event: 'input_event',
|
event: 'input_event',
|
||||||
data: editorContent.value
|
data: editorContent.value
|
||||||
@ -301,7 +310,7 @@ const handlePaste = (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理键盘事件
|
// 处理键盘事件 - 优化版本,减少不必要的日志和简化逻辑
|
||||||
const handleKeydown = (event) => {
|
const handleKeydown = (event) => {
|
||||||
// 处理@提及列表的键盘导航
|
// 处理@提及列表的键盘导航
|
||||||
if (showMention.value) {
|
if (showMention.value) {
|
||||||
@ -313,11 +322,8 @@ const handleKeydown = (event) => {
|
|||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const mentionList = document.querySelector('.mention-list ul')
|
const mentionList = document.querySelector('.mention-list ul')
|
||||||
const selectedItem = mentionList?.children[selectedMentionIndex.value]
|
const selectedItem = mentionList?.children[selectedMentionIndex.value]
|
||||||
if (mentionList && selectedItem) {
|
if (mentionList && selectedItem && selectedItem.offsetTop < mentionList.scrollTop) {
|
||||||
// 如果选中项在可视区域上方,滚动到选中项
|
mentionList.scrollTop = selectedItem.offsetTop
|
||||||
if (selectedItem.offsetTop < mentionList.scrollTop) {
|
|
||||||
mentionList.scrollTop = selectedItem.offsetTop
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
@ -329,7 +335,6 @@ const handleKeydown = (event) => {
|
|||||||
const mentionList = document.querySelector('.mention-list ul')
|
const mentionList = document.querySelector('.mention-list ul')
|
||||||
const selectedItem = mentionList?.children[selectedMentionIndex.value]
|
const selectedItem = mentionList?.children[selectedMentionIndex.value]
|
||||||
if (mentionList && selectedItem) {
|
if (mentionList && selectedItem) {
|
||||||
// 如果选中项在可视区域下方,滚动到选中项
|
|
||||||
const itemBottom = selectedItem.offsetTop + selectedItem.offsetHeight
|
const itemBottom = selectedItem.offsetTop + selectedItem.offsetHeight
|
||||||
const listBottom = mentionList.scrollTop + mentionList.clientHeight
|
const listBottom = mentionList.scrollTop + mentionList.clientHeight
|
||||||
if (itemBottom > listBottom) {
|
if (itemBottom > listBottom) {
|
||||||
@ -341,8 +346,9 @@ const handleKeydown = (event) => {
|
|||||||
case 'Enter':
|
case 'Enter':
|
||||||
case 'Tab':
|
case 'Tab':
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (mentionList.value[selectedMentionIndex.value]) {
|
const selectedMember = mentionList.value[selectedMentionIndex.value]
|
||||||
insertMention(mentionList.value[selectedMentionIndex.value])
|
if (selectedMember) {
|
||||||
|
insertMention(selectedMember)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
@ -351,146 +357,104 @@ const handleKeydown = (event) => {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('键盘事件:', event.key, 'Ctrl:', event.ctrlKey, 'Meta:', event.metaKey, 'Shift:', event.shiftKey);
|
|
||||||
|
|
||||||
// 处理Ctrl+Enter或Shift+Enter换行
|
// 处理Ctrl+Enter或Shift+Enter换行
|
||||||
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey || event.shiftKey)) {
|
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey || event.shiftKey)) {
|
||||||
console.log('Ctrl+Enter或Shift+Enter换行');
|
|
||||||
// 不阻止默认行为,允许插入换行符
|
|
||||||
// 手动插入换行符
|
// 手动插入换行符
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection()
|
||||||
if (selection && selection.rangeCount > 0) {
|
if (selection && selection.rangeCount > 0) {
|
||||||
const range = selection.getRangeAt(0);
|
const range = selection.getRangeAt(0)
|
||||||
const br = document.createElement('br');
|
const br = document.createElement('br')
|
||||||
range.deleteContents();
|
range.deleteContents()
|
||||||
range.insertNode(br);
|
range.insertNode(br)
|
||||||
|
|
||||||
// 在换行符后添加一个空文本节点,并将光标移动到这个节点
|
// 在换行符后添加一个空文本节点,并将光标移动到这个节点
|
||||||
const textNode = document.createTextNode('');
|
const textNode = document.createTextNode('')
|
||||||
range.setStartAfter(br);
|
range.setStartAfter(br)
|
||||||
range.insertNode(textNode);
|
range.insertNode(textNode)
|
||||||
range.setStartAfter(textNode);
|
range.setStartAfter(textNode)
|
||||||
range.collapse(true);
|
range.collapse(true)
|
||||||
selection.removeAllRanges();
|
selection.removeAllRanges()
|
||||||
selection.addRange(range);
|
selection.addRange(range)
|
||||||
|
|
||||||
// 触发输入事件更新编辑器内容
|
// 触发输入事件更新编辑器内容
|
||||||
handleInput({ target: editorRef.value });
|
handleInput({ target: editorRef.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 阻止默认行为,防止触发表单提交
|
// 阻止默认行为,防止触发表单提交
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理Enter键发送消息(只有在没有按Ctrl/Cmd/Shift时才发送)
|
// 处理Enter键发送消息(只有在没有按Ctrl/Cmd/Shift时才发送)
|
||||||
if (event.key === 'Enter' && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
|
if (event.key === 'Enter' && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
|
||||||
console.log('Enter发送消息');
|
event.preventDefault()
|
||||||
event.preventDefault();
|
|
||||||
console.log('editorContent.value', editorContent.value);
|
// 检查引用元素是否存在,如果不存在但 quoteData 有值,则清除 quoteData
|
||||||
console.log('editorHtml.value', editorHtml.value);
|
const editor = editorRef.value
|
||||||
// 确保编辑器内容不为空(文本、图片、文件或表情)
|
const quoteElement = editor?.querySelector('.editor-quote')
|
||||||
// 由于我们已经在 handleInput 中处理了表情文本,editorContent.value 应该包含表情文本
|
if (!quoteElement && quoteData.value) {
|
||||||
// if (editorContent.value.trim()) {
|
quoteData.value = null
|
||||||
if (true) {
|
|
||||||
// 检查引用元素是否存在,如果不存在但 quoteData 有值,则清除 quoteData
|
|
||||||
const editor = editorRef.value
|
|
||||||
const quoteElement = editor?.querySelector('.editor-quote')
|
|
||||||
if (!quoteElement && quoteData.value) {
|
|
||||||
console.log('引用元素已被删除,但 quoteData 仍有值,清除 quoteData')
|
|
||||||
quoteData.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析并输出编辑器内容
|
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ctrl+Enter换行由WangEditor的onKeyDown处理,这里不需要额外处理
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息
|
// 发送消息 - 优化版本,移除不必要的日志,简化逻辑
|
||||||
const sendMessage = () => {
|
const sendMessage = () => {
|
||||||
console.log('发送消息');
|
// 解析编辑器内容
|
||||||
// 检查编辑器是否有内容:文本内容(包括表情文本)
|
|
||||||
// if (!editorContent.value.trim()) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
console.log('发送消息1');
|
|
||||||
const messageData = parseEditorContent()
|
const messageData = parseEditorContent()
|
||||||
|
|
||||||
// 输出完整的消息数据结构
|
// 检查是否有内容可发送
|
||||||
console.log('完整消息数据:', {
|
if (messageData.items.length === 0 ||
|
||||||
items: messageData.items,
|
(messageData.items.length === 1 &&
|
||||||
mentionUids: messageData.mentionUids,
|
messageData.items[0].type === 1 &&
|
||||||
quoteId: messageData.quoteId,
|
!messageData.items[0].content.trim())) {
|
||||||
quoteData: quoteData.value ? {
|
return // 没有内容,不发送
|
||||||
id: quoteData.value.id,
|
}
|
||||||
title: quoteData.value.title,
|
|
||||||
describe: quoteData.value.describe,
|
// 处理不同类型的消息
|
||||||
image: quoteData.value.image
|
|
||||||
} : null
|
|
||||||
})
|
|
||||||
messageData.items.forEach(item => {
|
messageData.items.forEach(item => {
|
||||||
// 处理文本内容
|
// 处理文本内容
|
||||||
if (item.type === 1) {
|
if (item.type === 1 && item.content.trim()) {
|
||||||
const data={
|
const data = {
|
||||||
items:[{
|
items: [{
|
||||||
content:item.content,
|
content: item.content,
|
||||||
type:1
|
type: 1
|
||||||
}],
|
}],
|
||||||
mentionUids:messageData.mentionUids,
|
mentionUids: messageData.mentionUids,
|
||||||
mentions:[],
|
mentions: [],
|
||||||
quoteId:messageData.quoteId,
|
quoteId: messageData.quoteId,
|
||||||
}
|
}
|
||||||
console.log('发送前',data)
|
|
||||||
console.log('quoteData',quoteData.value)
|
|
||||||
emit(
|
emit(
|
||||||
'editor-event',
|
'editor-event',
|
||||||
emitCall('text_event', data,(ok)=>{
|
emitCall('text_event', data)
|
||||||
console.log('发送后',ok)
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
}else if(item.type === 2){
|
} else if (item.type === 3) { // 图片消息
|
||||||
//图片消息
|
const data = {
|
||||||
}else if(item.type === 3){
|
height: 0,
|
||||||
console.log('发送图片消息')
|
width: 0,
|
||||||
const data={
|
size: 10000,
|
||||||
height:0,
|
url: item.content,
|
||||||
width:0,
|
|
||||||
size:10000,
|
|
||||||
url:item.content,
|
|
||||||
}
|
|
||||||
emit(
|
|
||||||
'editor-event',
|
|
||||||
emitCall(
|
|
||||||
'image_event',
|
|
||||||
data,
|
|
||||||
(ok) => {
|
|
||||||
// 成功发送后清空编辑器
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}else if(item.type === 4){
|
|
||||||
|
|
||||||
}
|
}
|
||||||
clearEditor()
|
|
||||||
|
emit(
|
||||||
|
'editor-event',
|
||||||
|
emitCall('image_event', data)
|
||||||
|
)
|
||||||
|
} else if (item.type === 4) { // 文件消息
|
||||||
|
// 文件消息处理逻辑
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 发送后清空编辑器
|
||||||
|
clearEditor()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析编辑器内容
|
// 解析编辑器内容
|
||||||
const parseEditorContent = () => {
|
const parseEditorContent = () => {
|
||||||
const items = []
|
const items = []
|
||||||
const mentionUids = []
|
const mentionUids = []
|
||||||
@ -501,102 +465,120 @@ const parseEditorContent = () => {
|
|||||||
|
|
||||||
// 检查是否有引用元素
|
// 检查是否有引用元素
|
||||||
const quoteElements = tempDiv.querySelectorAll('.editor-quote')
|
const quoteElements = tempDiv.querySelectorAll('.editor-quote')
|
||||||
let quoteInfo = null
|
const hasQuote = quoteElements.length > 0 && quoteData.value
|
||||||
if (quoteElements.length > 0 && quoteData.value) {
|
const quoteId = hasQuote ? quoteData.value.id || '' : ''
|
||||||
quoteInfo = {
|
|
||||||
id: quoteData.value.id,
|
// 移除引用元素,避免重复处理
|
||||||
title: quoteData.value.title,
|
quoteElements.forEach(quote => quote.remove())
|
||||||
describe: quoteData.value.describe,
|
|
||||||
image: quoteData.value.image
|
let textContent = ''
|
||||||
|
|
||||||
|
// 处理文本节点和元素节点
|
||||||
|
const processNode = (node) => {
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
// 文本节点直接添加内容
|
||||||
|
textContent += node.textContent
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodeType !== Node.ELEMENT_NODE) return
|
||||||
|
|
||||||
|
// 处理不同类型的元素
|
||||||
|
if (node.classList.contains('mention')) {
|
||||||
|
// 处理@提及
|
||||||
|
const userId = node.getAttribute('data-user-id')
|
||||||
|
if (userId) {
|
||||||
|
mentionUids.push(parseInt(userId))
|
||||||
|
}
|
||||||
|
textContent += node.textContent
|
||||||
|
} else if (node.tagName === 'IMG') {
|
||||||
|
// 处理图片
|
||||||
|
processImage(node)
|
||||||
|
} else if (node.classList.contains('emoji')) {
|
||||||
|
// 处理emoji元素
|
||||||
|
textContent += node.getAttribute('alt') || node.textContent
|
||||||
|
} else if (node.classList.contains('editor-file')) {
|
||||||
|
// 处理文件
|
||||||
|
processFile(node)
|
||||||
|
} else if (node.childNodes.length) {
|
||||||
|
// 处理有子节点的元素
|
||||||
|
Array.from(node.childNodes).forEach(processNode)
|
||||||
|
} else {
|
||||||
|
// 其他元素,添加文本内容
|
||||||
|
textContent += node.textContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let textContent = ''
|
// 处理图片元素
|
||||||
const nodes = Array.from(tempDiv.childNodes)
|
const processImage = (node) => {
|
||||||
|
const src = node.getAttribute('src')
|
||||||
nodes.forEach(node => {
|
const width = node.getAttribute('data-original-width') || node.getAttribute('width') || ''
|
||||||
if (node.nodeType === Node.TEXT_NODE) {
|
const height = node.getAttribute('data-original-height') || node.getAttribute('height') || ''
|
||||||
textContent += node.textContent
|
const isEmoji = node.classList.contains('editor-emoji')
|
||||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
|
||||||
// 跳过引用元素的处理,因为我们已经单独处理了
|
// 如果有累积的文本内容,先添加到items
|
||||||
if (node.classList.contains('editor-quote')) {
|
if (textContent.trim()) {
|
||||||
return
|
items.push({
|
||||||
}
|
type: 1,
|
||||||
|
content: textContent.trim()
|
||||||
if (node.classList.contains('mention')) {
|
})
|
||||||
const userId = node.getAttribute('data-user-id')
|
textContent = ''
|
||||||
if (userId) {
|
|
||||||
mentionUids.push(parseInt(userId))
|
|
||||||
}
|
|
||||||
textContent += node.textContent
|
|
||||||
} else if (node.tagName === 'IMG') {
|
|
||||||
// 处理图片
|
|
||||||
const src = node.getAttribute('src')
|
|
||||||
const width = node.getAttribute('data-original-width') || node.getAttribute('width') || ''
|
|
||||||
const height = node.getAttribute('data-original-height') || node.getAttribute('height') || ''
|
|
||||||
const isEmoji = node.classList.contains('editor-emoji')
|
|
||||||
|
|
||||||
if (textContent.trim()) {
|
|
||||||
items.push({
|
|
||||||
type: 1,
|
|
||||||
content: textContent.trim()
|
|
||||||
})
|
|
||||||
textContent = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isEmoji) {
|
|
||||||
// 处理表情图片
|
|
||||||
const altText = node.getAttribute('alt') || ''
|
|
||||||
if (altText) {
|
|
||||||
// 如果有alt文本,将表情作为文本处理
|
|
||||||
textContent += altText
|
|
||||||
} else {
|
|
||||||
// 否则作为图片处理
|
|
||||||
items.push({
|
|
||||||
type: 3,
|
|
||||||
content: src + (width && height ? `?width=${width}&height=${height}` : ''),
|
|
||||||
isEmoji: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 处理普通图片
|
|
||||||
items.push({
|
|
||||||
type: 3,
|
|
||||||
content: src + (width && height ? `?width=${width}&height=${height}` : ''),
|
|
||||||
width: width,
|
|
||||||
height: height
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else if (node.classList.contains('emoji')) {
|
|
||||||
textContent += node.getAttribute('alt') || node.textContent
|
|
||||||
} else if (node.classList.contains('editor-file')) {
|
|
||||||
// 处理文件
|
|
||||||
const fileUrl = node.getAttribute('data-url')
|
|
||||||
const fileName = node.getAttribute('data-name')
|
|
||||||
const fileSize = node.getAttribute('data-size')
|
|
||||||
|
|
||||||
if (textContent.trim()) {
|
|
||||||
items.push({
|
|
||||||
type: 1,
|
|
||||||
content: textContent.trim()
|
|
||||||
})
|
|
||||||
textContent = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileUrl && fileName) {
|
|
||||||
items.push({
|
|
||||||
type: 'file',
|
|
||||||
content: fileUrl,
|
|
||||||
name: fileName,
|
|
||||||
size: node.getAttribute('data-size-raw') || fileSize || 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
textContent += node.textContent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
if (isEmoji) {
|
||||||
|
// 处理表情图片
|
||||||
|
const altText = node.getAttribute('alt') || ''
|
||||||
|
if (altText) {
|
||||||
|
// 如果有alt文本,将表情作为文本处理
|
||||||
|
textContent += altText
|
||||||
|
} else {
|
||||||
|
// 否则作为图片处理
|
||||||
|
items.push({
|
||||||
|
type: 3,
|
||||||
|
content: src + (width && height ? `?width=${width}&height=${height}` : ''),
|
||||||
|
isEmoji: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 处理普通图片
|
||||||
|
items.push({
|
||||||
|
type: 3,
|
||||||
|
content: src + (width && height ? `?width=${width}&height=${height}` : ''),
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件元素
|
||||||
|
const processFile = (node) => {
|
||||||
|
const fileUrl = node.getAttribute('data-url')
|
||||||
|
const fileName = node.getAttribute('data-name')
|
||||||
|
const fileSize = node.getAttribute('data-size')
|
||||||
|
|
||||||
|
// 如果有累积的文本内容,先添加到items
|
||||||
|
if (textContent.trim()) {
|
||||||
|
items.push({
|
||||||
|
type: 1,
|
||||||
|
content: textContent.trim()
|
||||||
|
})
|
||||||
|
textContent = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileUrl && fileName) {
|
||||||
|
items.push({
|
||||||
|
type: 4, // 使用数字类型保持一致性
|
||||||
|
content: fileUrl,
|
||||||
|
name: fileName,
|
||||||
|
size: node.getAttribute('data-size-raw') || fileSize || 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理所有顶级节点
|
||||||
|
Array.from(tempDiv.childNodes).forEach(processNode)
|
||||||
|
|
||||||
|
// 处理剩余的文本内容
|
||||||
if (textContent.trim()) {
|
if (textContent.trim()) {
|
||||||
items.push({
|
items.push({
|
||||||
type: 1,
|
type: 1,
|
||||||
@ -605,30 +587,28 @@ const parseEditorContent = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 构建完整的消息数据结构
|
// 构建完整的消息数据结构
|
||||||
const result = {
|
return {
|
||||||
items: items.length > 0 ? items : [{ type: 1, content: '' }],
|
items: items.length > 0 ? items : [{ type: 1, content: '' }],
|
||||||
mentionUids,
|
mentionUids,
|
||||||
quoteId: quoteElements.length > 0 && quoteData.value ? quoteData.value.id ||'' : ''
|
quoteId
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有引用信息,添加到结果中
|
|
||||||
if (quoteInfo) {
|
|
||||||
result.quoteInfo = quoteInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空编辑器
|
// 清空编辑器
|
||||||
const clearEditor = () => {
|
const clearEditor = () => {
|
||||||
|
// 一次性清空所有编辑器相关状态
|
||||||
editorContent.value = ''
|
editorContent.value = ''
|
||||||
editorHtml.value = ''
|
editorHtml.value = ''
|
||||||
|
quoteData.value = null
|
||||||
|
|
||||||
|
// 清空DOM内容
|
||||||
if (editorRef.value) {
|
if (editorRef.value) {
|
||||||
editorRef.value.innerHTML = ''
|
editorRef.value.innerHTML = ''
|
||||||
|
// 立即设置焦点,提高响应速度
|
||||||
|
nextTick(() => editorRef.value.focus())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除引用数据
|
// 隐藏@提及列表
|
||||||
quoteData.value = null
|
|
||||||
hideMentionList()
|
hideMentionList()
|
||||||
|
|
||||||
// 清空草稿
|
// 清空草稿
|
||||||
@ -655,8 +635,8 @@ const insertImage = (src, width, height) => {
|
|||||||
img.src = src
|
img.src = src
|
||||||
img.className = 'editor-image'
|
img.className = 'editor-image'
|
||||||
img.alt = '图片'
|
img.alt = '图片'
|
||||||
img.style.maxHeight = '200px'
|
img.style.maxHeight = '150px'
|
||||||
img.style.maxWidth = '100%'
|
img.style.maxWidth = '150px'
|
||||||
img.style.objectFit = 'contain' // 保持原始比例
|
img.style.objectFit = 'contain' // 保持原始比例
|
||||||
|
|
||||||
// 存储原始尺寸信息,但不直接设置宽高属性
|
// 存储原始尺寸信息,但不直接设置宽高属性
|
||||||
@ -712,30 +692,25 @@ const onUploadSendImg=async (eventFile)=>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function onUploadFile(e) {
|
async function onUploadFile(e) {
|
||||||
let file = e.target.files[0]
|
const file = e.target.files[0]
|
||||||
|
if (!file) return
|
||||||
e.target.value = null // 清空input,允许再次选择相同文件
|
|
||||||
|
// 清空input,允许再次选择相同文件
|
||||||
console.log("文件类型"+file.type)
|
e.target.value = null
|
||||||
|
|
||||||
if (file.type.indexOf('image/') === 0) {
|
if (file.type.indexOf('image/') === 0) {
|
||||||
console.log("进入图片")
|
|
||||||
// 处理图片文件 - 立即显示临时消息,然后上传
|
// 处理图片文件 - 立即显示临时消息,然后上传
|
||||||
let fn = emitCall('image_event', file, () => {})
|
emit('editor-event', emitCall('image_event', file))
|
||||||
emit('editor-event', fn)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.type.indexOf('video/') === 0) {
|
if (file.type.indexOf('video/') === 0) {
|
||||||
console.log("进入视频")
|
|
||||||
// 处理视频文件
|
// 处理视频文件
|
||||||
let fn = emitCall('video_event', file, () => {})
|
emit('editor-event', emitCall('video_event', file))
|
||||||
emit('editor-event', fn)
|
|
||||||
} else {
|
} else {
|
||||||
console.log("进入其他")
|
|
||||||
// 处理其他类型文件
|
// 处理其他类型文件
|
||||||
let fn = emitCall('file_event', file, () => {})
|
emit('editor-event', emitCall('file_event', file))
|
||||||
emit('editor-event', fn)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,29 +720,29 @@ const onEmoticonEvent = (emoji) => {
|
|||||||
emoticonRef.value?.setShow(false)
|
emoticonRef.value?.setShow(false)
|
||||||
|
|
||||||
// 处理不同类型的表情
|
// 处理不同类型的表情
|
||||||
if (emoji.type === 'text') {
|
switch (emoji.type) {
|
||||||
// 文本表情
|
case 'text':
|
||||||
insertTextEmoji(emoji.value)
|
case 'emoji':
|
||||||
} else if (emoji.type === 'image') {
|
// 文本表情和系统表情都使用相同的处理方式
|
||||||
// 图片表情
|
|
||||||
insertImageEmoji(emoji.img, emoji.value)
|
|
||||||
} else if (emoji.type === 'emoji') {
|
|
||||||
// 系统表情
|
|
||||||
insertTextEmoji(emoji.value)
|
|
||||||
} else if (emoji.type === 1) {
|
|
||||||
// 兼容旧版表情格式
|
|
||||||
if (emoji.img) {
|
|
||||||
insertImageEmoji(emoji.img, emoji.value)
|
|
||||||
} else {
|
|
||||||
insertTextEmoji(emoji.value)
|
insertTextEmoji(emoji.value)
|
||||||
}
|
break
|
||||||
} else {
|
|
||||||
// 发送整个表情包
|
case 'image':
|
||||||
emit('editor-event', {
|
// 图片表情
|
||||||
event: 'emoticon_event',
|
insertImageEmoji(emoji.img, emoji.value)
|
||||||
data: emoji.value || emoji.id,
|
break
|
||||||
callBack: () => {}
|
|
||||||
})
|
case 1: // 兼容旧版表情格式
|
||||||
|
emoji.img ? insertImageEmoji(emoji.img, emoji.value) : insertTextEmoji(emoji.value)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
// 发送整个表情包
|
||||||
|
emit('editor-event', {
|
||||||
|
event: 'emoticon_event',
|
||||||
|
data: emoji.value || emoji.id
|
||||||
|
})
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,28 +867,23 @@ const onSubscribeQuote = (data) => {
|
|||||||
|
|
||||||
// 使用事件委托处理引用元素的所有点击事件
|
// 使用事件委托处理引用元素的所有点击事件
|
||||||
quoteElement.addEventListener('click', (e) => {
|
quoteElement.addEventListener('click', (e) => {
|
||||||
console.log('执行删除',e)
|
|
||||||
// 检查点击的是否是关闭按钮或其内部元素
|
// 检查点击的是否是关闭按钮或其内部元素
|
||||||
const closeButton = e.target.classList?.contains('quote-close') ? e.target : e.target.closest('.quote-close')
|
const closeButton = e.target.classList?.contains('quote-close') ? e.target : e.target.closest('.quote-close')
|
||||||
|
|
||||||
|
// 阻止事件冒泡,避免触发编辑器的其他点击事件
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
if (closeButton) {
|
if (closeButton) {
|
||||||
// 阻止事件冒泡
|
// 移除引用元素并清除引用数据
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
// 移除引用元素
|
|
||||||
quoteElement.remove()
|
quoteElement.remove()
|
||||||
|
|
||||||
// 清除引用数据
|
|
||||||
quoteData.value = null
|
quoteData.value = null
|
||||||
|
|
||||||
// 不触发handleInput,避免保存草稿
|
// 只更新编辑器内容变量,不触发handleInput,避免保存草稿
|
||||||
// 只更新编辑器内容变量
|
|
||||||
editorContent.value = editor.textContent || ''
|
editorContent.value = editor.textContent || ''
|
||||||
editorHtml.value = editor.innerHTML || ''
|
editorHtml.value = editor.innerHTML || ''
|
||||||
|
|
||||||
// 确保编辑器获得焦点
|
// 使用nextTick确保DOM更新后再设置焦点
|
||||||
setTimeout(() => {
|
nextTick(() => editor.focus())
|
||||||
editor.focus()
|
|
||||||
}, 0)
|
|
||||||
} else {
|
} else {
|
||||||
// 如果不是点击关闭按钮,则设置光标到引用卡片后面
|
// 如果不是点击关闭按钮,则设置光标到引用卡片后面
|
||||||
const selection = window.getSelection()
|
const selection = window.getSelection()
|
||||||
@ -923,7 +893,7 @@ const onSubscribeQuote = (data) => {
|
|||||||
selection.removeAllRanges()
|
selection.removeAllRanges()
|
||||||
selection.addRange(range)
|
selection.addRange(range)
|
||||||
|
|
||||||
// 确保编辑器获得焦点
|
// 立即设置焦点
|
||||||
editor.focus()
|
editor.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -935,11 +905,11 @@ const onSubscribeQuote = (data) => {
|
|||||||
// 这里只需要处理非关闭按钮的点击
|
// 这里只需要处理非关闭按钮的点击
|
||||||
// 注意:由于事件委托的方式,不需要额外添加点击事件监听器
|
// 注意:由于事件委托的方式,不需要额外添加点击事件监听器
|
||||||
|
|
||||||
// 监听键盘事件,处理删除操作
|
// 监听键盘事件,处理引用元素的删除操作
|
||||||
// 监听键盘事件,处理删除操作
|
|
||||||
const handleDeleteQuote = function(e) {
|
const handleDeleteQuote = function(e) {
|
||||||
// 检查是否是删除键(Backspace 或 Delete)
|
// 只处理删除键(Backspace 或 Delete)
|
||||||
if (e.key === 'Backspace' || e.key === 'Delete') {
|
if (e.key !== 'Backspace' && e.key !== 'Delete') return;
|
||||||
|
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
if (selection.rangeCount === 0) return;
|
if (selection.rangeCount === 0) return;
|
||||||
|
|
||||||
@ -952,25 +922,32 @@ const onSubscribeQuote = (data) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取引用元素在子节点中的索引
|
||||||
|
const quoteIndex = Array.from(editor.childNodes).indexOf(quoteElement);
|
||||||
|
|
||||||
// 检查光标是否在引用卡片前面(Backspace)或后面(Delete)
|
// 检查光标是否在引用卡片前面(Backspace)或后面(Delete)
|
||||||
const isBeforeQuote = e.key === 'Backspace' &&
|
const isBeforeQuote = e.key === 'Backspace' &&
|
||||||
range.collapsed &&
|
range.collapsed &&
|
||||||
range.startContainer === editor &&
|
range.startContainer === editor &&
|
||||||
Array.from(editor.childNodes).indexOf(quoteElement) === range.startOffset;
|
quoteIndex === range.startOffset;
|
||||||
|
|
||||||
const isAfterQuote = e.key === 'Delete' &&
|
const isAfterQuote = e.key === 'Delete' &&
|
||||||
range.collapsed &&
|
range.collapsed &&
|
||||||
range.startContainer === editor &&
|
range.startContainer === editor &&
|
||||||
Array.from(editor.childNodes).indexOf(quoteElement) === range.startOffset - 1;
|
quoteIndex === range.startOffset - 1;
|
||||||
|
|
||||||
if (isBeforeQuote || isAfterQuote) {
|
if (isBeforeQuote || isAfterQuote) {
|
||||||
|
// 阻止默认删除行为
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// 移除引用元素并清除引用数据
|
||||||
quoteElement.remove();
|
quoteElement.remove();
|
||||||
quoteData.value = null;
|
quoteData.value = null;
|
||||||
|
|
||||||
|
// 更新编辑器内容
|
||||||
handleInput({ target: editor });
|
handleInput({ target: editor });
|
||||||
e.preventDefault();
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
editor.addEventListener('keydown', handleDeleteQuote);
|
editor.addEventListener('keydown', handleDeleteQuote);
|
||||||
|
|
||||||
@ -1062,38 +1039,34 @@ const onSubscribeClear = () => {
|
|||||||
|
|
||||||
// 保存草稿
|
// 保存草稿
|
||||||
const saveDraft = () => {
|
const saveDraft = () => {
|
||||||
if (!indexName.value) return
|
if (!indexName.value || !editorRef.value) return
|
||||||
|
|
||||||
// 获取编辑器内容,但排除引用元素
|
// 获取编辑器内容,但排除引用元素
|
||||||
let contentToSave = ''
|
// 使用DocumentFragment进行高效的DOM操作
|
||||||
let htmlToSave = ''
|
const fragment = document.createDocumentFragment()
|
||||||
|
const tempDiv = document.createElement('div')
|
||||||
|
tempDiv.innerHTML = editorRef.value.innerHTML
|
||||||
|
fragment.appendChild(tempDiv)
|
||||||
|
|
||||||
if (editorRef.value) {
|
// 从临时DOM中移除引用元素
|
||||||
// 临时保存引用元素
|
const quoteElements = tempDiv.querySelectorAll('.editor-quote')
|
||||||
const quoteElements = []
|
quoteElements.forEach(quote => quote.remove())
|
||||||
const editorQuotes = editorRef.value.querySelectorAll('.editor-quote')
|
|
||||||
|
// 获取不包含引用的内容
|
||||||
// 克隆编辑器内容
|
const contentToSave = tempDiv.textContent || ''
|
||||||
const clonedEditor = editorRef.value.cloneNode(true)
|
const htmlToSave = tempDiv.innerHTML || ''
|
||||||
|
|
||||||
// 从克隆的编辑器中移除引用元素
|
|
||||||
const clonedQuotes = clonedEditor.querySelectorAll('.editor-quote')
|
|
||||||
clonedQuotes.forEach(quote => quote.remove())
|
|
||||||
|
|
||||||
// 获取不包含引用的内容
|
|
||||||
contentToSave = clonedEditor.textContent || ''
|
|
||||||
htmlToSave = clonedEditor.innerHTML || ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有实际内容(不包括引用)
|
// 检查是否有实际内容(不包括引用)
|
||||||
const hasContent = contentToSave.trim() || htmlToSave.includes('<img') || htmlToSave.includes('editor-file')
|
const hasContent = contentToSave.trim().length > 0 ||
|
||||||
|
htmlToSave.includes('<img') ||
|
||||||
|
htmlToSave.includes('editor-file')
|
||||||
|
|
||||||
|
// 根据内容状态保存或删除草稿
|
||||||
if (hasContent) {
|
if (hasContent) {
|
||||||
// 保存草稿到store,不包括引用数据
|
// 保存草稿到store,不包括引用数据
|
||||||
editorDraftStore.items[indexName.value] = JSON.stringify({
|
editorDraftStore.items[indexName.value] = JSON.stringify({
|
||||||
content: contentToSave,
|
content: contentToSave,
|
||||||
html: htmlToSave
|
html: htmlToSave
|
||||||
// 不保存quoteData,确保引用不会出现在草稿中
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// 编辑器为空时删除对应草稿
|
// 编辑器为空时删除对应草稿
|
||||||
@ -1101,80 +1074,80 @@ const saveDraft = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载草稿
|
// 加载草稿
|
||||||
const loadDraft = () => {
|
const loadDraft = () => {
|
||||||
if (!indexName.value) return
|
if (!indexName.value) return
|
||||||
|
|
||||||
// 延迟处理,确保DOM已渲染
|
// 使用nextTick确保DOM已渲染,更可预测且性能更好
|
||||||
setTimeout(() => {
|
nextTick(() => {
|
||||||
// 保存当前引用数据的临时副本
|
// 保存当前引用数据的临时副本
|
||||||
const currentQuoteData = quoteData.value
|
const currentQuoteData = quoteData.value
|
||||||
|
|
||||||
// 清除当前引用数据,避免重复添加
|
// 清除当前引用数据,避免重复添加
|
||||||
quoteData.value = null
|
quoteData.value = null
|
||||||
|
|
||||||
|
// 如果编辑器引用不存在,直接返回
|
||||||
|
if (!editorRef.value) return
|
||||||
|
|
||||||
|
// 先清空编辑器内容
|
||||||
|
editorRef.value.innerHTML = ''
|
||||||
|
editorContent.value = ''
|
||||||
|
editorHtml.value = ''
|
||||||
|
|
||||||
|
// 获取草稿数据
|
||||||
const draft = editorDraftStore.items[indexName.value]
|
const draft = editorDraftStore.items[indexName.value]
|
||||||
|
|
||||||
|
// 如果有草稿,恢复草稿内容
|
||||||
if (draft) {
|
if (draft) {
|
||||||
try {
|
try {
|
||||||
const draftData = JSON.parse(draft)
|
const draftData = JSON.parse(draft)
|
||||||
|
|
||||||
// 恢复编辑器内容(不包含引用)
|
// 恢复草稿内容
|
||||||
if (editorRef.value) {
|
editorRef.value.innerHTML = draftData.html || ''
|
||||||
// 先清空编辑器内容,包括引用元素
|
editorContent.value = draftData.content || ''
|
||||||
editorRef.value.innerHTML = ''
|
editorHtml.value = draftData.html || ''
|
||||||
|
|
||||||
// 恢复草稿内容
|
|
||||||
editorRef.value.innerHTML = draftData.html || ''
|
|
||||||
editorContent.value = draftData.content || ''
|
|
||||||
editorHtml.value = draftData.html || ''
|
|
||||||
|
|
||||||
// 如果有引用数据,重新添加到编辑器
|
|
||||||
if (currentQuoteData) {
|
|
||||||
// 重新调用引用函数添加引用元素
|
|
||||||
onSubscribeQuote(currentQuoteData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载草稿失败:', error)
|
console.warn('加载草稿失败,使用空内容', error)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 没有草稿则清空编辑器内容,但保留引用
|
|
||||||
if (editorRef.value) {
|
|
||||||
// 清空编辑器
|
|
||||||
editorRef.value.innerHTML = ''
|
|
||||||
editorContent.value = ''
|
|
||||||
editorHtml.value = ''
|
|
||||||
|
|
||||||
// 如果有引用数据,重新添加到编辑器
|
|
||||||
if (currentQuoteData) {
|
|
||||||
onSubscribeQuote(currentQuoteData)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 0)
|
|
||||||
|
// 如果有引用数据,重新添加到编辑器(无论是否有草稿)
|
||||||
|
if (currentQuoteData) {
|
||||||
|
onSubscribeQuote(currentQuoteData)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听会话变化,加载对应草稿
|
// 监听会话变化,加载对应草稿
|
||||||
watch(indexName, loadDraft, { immediate: true })
|
watch(indexName, loadDraft, { immediate: true })
|
||||||
|
|
||||||
|
// 处理点击文档事件,隐藏@提及列表
|
||||||
|
const handleDocumentClick = (event) => {
|
||||||
|
if (!editorRef.value?.contains(event.target)) {
|
||||||
|
hideMentionList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 组件挂载
|
// 组件挂载
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
bus.subscribe(EditorConst.Mention, onSubscribeMention)
|
// 订阅所有编辑器相关事件
|
||||||
bus.subscribe(EditorConst.Quote, onSubscribeQuote)
|
const subscriptions = [
|
||||||
bus.subscribe(EditorConst.Edit, onSubscribeEdit)
|
[EditorConst.Mention, onSubscribeMention],
|
||||||
bus.subscribe(EditorConst.Clear, onSubscribeClear)
|
[EditorConst.Quote, onSubscribeQuote],
|
||||||
|
[EditorConst.Edit, onSubscribeEdit],
|
||||||
// 为编辑器添加点击事件监听器,用于处理引用消息关闭等
|
[EditorConst.Clear, onSubscribeClear]
|
||||||
if (editorRef.value) {
|
]
|
||||||
editorRef.value.addEventListener('click', handleEditorClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击外部隐藏mention
|
// 批量订阅事件
|
||||||
document.addEventListener('click', (event) => {
|
subscriptions.forEach(([event, handler]) => {
|
||||||
if (!editorRef.value?.contains(event.target)) {
|
bus.subscribe(event, handler)
|
||||||
hideMentionList()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 为编辑器添加点击事件监听器
|
||||||
|
editorRef.value?.addEventListener('click', handleEditorClick)
|
||||||
|
|
||||||
|
// 点击外部隐藏mention - 使用命名函数便于清理
|
||||||
|
document.addEventListener('click', handleDocumentClick)
|
||||||
|
|
||||||
// 初始加载草稿
|
// 初始加载草稿
|
||||||
loadDraft()
|
loadDraft()
|
||||||
@ -1182,24 +1155,27 @@ onMounted(() => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件生命周期钩子 - 组件卸载前
|
* 组件生命周期钩子 - 组件卸载前
|
||||||
*
|
* 清理所有事件订阅和监听器,防止内存泄漏
|
||||||
* onBeforeUnmount是Vue 3的生命周期钩子,在组件卸载前执行
|
|
||||||
* 在这里用于清理事件订阅,防止内存泄漏
|
|
||||||
* 使用bus.unsubscribe取消订阅之前通过bus.subscribe注册的事件处理函数
|
|
||||||
*/
|
*/
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
// 取消订阅所有编辑器相关事件,防止内存泄漏和事件监听器残留
|
// 取消订阅所有编辑器相关事件
|
||||||
bus.unsubscribe(EditorConst.Mention, onSubscribeMention)
|
const subscriptions = [
|
||||||
bus.unsubscribe(EditorConst.Quote, onSubscribeQuote)
|
[EditorConst.Mention, onSubscribeMention],
|
||||||
bus.unsubscribe(EditorConst.Edit, onSubscribeEdit)
|
[EditorConst.Quote, onSubscribeQuote],
|
||||||
bus.unsubscribe(EditorConst.Clear, onSubscribeClear)
|
[EditorConst.Edit, onSubscribeEdit],
|
||||||
|
[EditorConst.Clear, onSubscribeClear]
|
||||||
|
]
|
||||||
|
|
||||||
|
// 批量取消订阅
|
||||||
|
subscriptions.forEach(([event, handler]) => {
|
||||||
|
bus.unsubscribe(event, handler)
|
||||||
|
})
|
||||||
|
|
||||||
// 移除编辑器点击事件监听器
|
// 移除编辑器点击事件监听器
|
||||||
if (editorRef.value) {
|
editorRef.value?.removeEventListener('click', handleEditorClick)
|
||||||
editorRef.value.removeEventListener('click', handleEditorClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清理DOM事件监听器
|
// 移除文档点击事件监听器
|
||||||
|
document.removeEventListener('click', handleDocumentClick)
|
||||||
const editor = editorRef.value
|
const editor = editorRef.value
|
||||||
if (editor && handleDeleteQuote) {
|
if (editor && handleDeleteQuote) {
|
||||||
editor.removeEventListener('keydown', handleDeleteQuote)
|
editor.removeEventListener('keydown', handleDeleteQuote)
|
||||||
@ -1729,16 +1705,16 @@ const handleEditorClick = (event) => {
|
|||||||
* 限制编辑器中插入的图片大小
|
* 限制编辑器中插入的图片大小
|
||||||
* 添加圆角和鼠标指针样式
|
* 添加圆角和鼠标指针样式
|
||||||
*/
|
*/
|
||||||
.editor-image {
|
// .editor-image {
|
||||||
max-width: 300px;
|
// max-width: 300px;
|
||||||
max-height: 200px;
|
// 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; /* 保持原始比例 */
|
// object-fit: contain; /* 保持原始比例 */
|
||||||
display: inline-block; /* 确保图片正确显示 */
|
// display: inline-block; /* 确保图片正确显示 */
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表情样式
|
* 表情样式
|
||||||
|
@ -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'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22403363066ad3c046839f7b2cf8a6186da017388f197c0c3b219b1c04e7d986e9774b72664a22a6075cee77da3584b7a2131365913796a5fcabc8f4594284e480a592a84a40a9aa7f5f27c951a53a369c'
|
return JSON.parse(localStorage.getItem('token'))||'46d71a72d8d845ad7ed23eba9bdde260e635407190c2ce1bf7fd22088e41682ea07773ec65cae8946d2003f264d55961f96e0fc5da10eb96d3a348c1664e9644ce2108c311309f398ae8ea1b8200bfd490e5cb6e8c52c9e5d493cbabb163368f8351420451a631dbfa749829ee4cda49b77b5ed2d3dced5d0f2b7dd9ee76ba5465c84a17c23af040cd92b6b2a4ea48befbb5c729dcdad0a9c9668befe84074cc24f78899c1d947f8e7f94c7eda5325b8ed698df729e76febb98549ef3482ae942fb4f4a1c92d21836fa784728f0c5483aab2760a991b6b36e6b10c84f840a6433a6ecc31dee36e8f1c6158818bc89d22c9c2f9b60a57573e8b08cdf47105e1ba85550c21fa55526e8a00bf316c623eb67abf749622c48beab908d61d3db7b22ed3eb6aa8a08c77680ad4d8a3458c1e72f97ba2b8480674df77f0501a34e82b58'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user