diff --git a/src/components/RichTextEditor.vue b/src/components/RichTextEditor.vue index 143c676..8e0130d 100644 --- a/src/components/RichTextEditor.vue +++ b/src/components/RichTextEditor.vue @@ -45,17 +45,106 @@ const dragState = ref({ lastMoveTime: 0, }) +// 统一的事件监听器管理器 +const eventManager = { + // 为图片容器添加事件监听器 + addImageContainerListeners(container, deleteBtn) { + // 先移除可能已有的事件监听器,避免重复 + this.removeImageContainerListeners(container) + + // 添加触摸事件监听器 + container.addEventListener('touchstart', handleTouchStart) + container.addEventListener('touchmove', handleTouchMove) + container.addEventListener('touchend', handleTouchEnd) + container.addEventListener('touchcancel', handleTouchCancel) + + // 为删除按钮添加点击事件 + if (deleteBtn) { + // 添加延时检查,确保删除按钮是可见的才执行删除操作 + setTimeout(() => { + // 保存事件处理函数的引用,以便后续移除 + const deleteHandler = function (e) { + e.stopPropagation() + if (deleteBtn.classList.contains('visible')) { + container.remove() + handleInput() + } + } + + deleteBtn.addEventListener('click', deleteHandler) + deleteBtn._deleteHandler = deleteHandler + }, DELETE_BUTTON_DELAY) + } + + // 为图片容器添加短按事件以显示/隐藏删除按钮 + let touchStartTime = 0 + const touchStartHandler = function (e) { + touchStartTime = Date.now() + } + + const touchEndHandler = function (e) { + const touchDuration = Date.now() - touchStartTime + // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 + if (touchDuration < 200 && !dragState.value.isLongPress) { + e.stopPropagation() + // 切换删除按钮的显示状态 + if (deleteBtn) { + const isCurrentlyVisible = deleteBtn.classList.contains('visible') + if (isCurrentlyVisible) { + deleteBtn.classList.remove('visible') + } else { + deleteBtn.classList.add('visible') + } + } + } + } + + container.addEventListener('touchstart', touchStartHandler) + container.addEventListener('touchend', touchEndHandler) + + // 保存事件处理函数的引用,以便后续移除 + container._touchStartHandler = touchStartHandler + container._touchEndHandler = touchEndHandler + }, + + // 移除图片容器的事件监听器 + removeImageContainerListeners(container) { + // 移除拖拽事件监听器 + container.removeEventListener('touchstart', handleTouchStart) + container.removeEventListener('touchmove', handleTouchMove) + container.removeEventListener('touchend', handleTouchEnd) + container.removeEventListener('touchcancel', handleTouchCancel) + + // 移除短按事件监听器 + const touchStartHandler = container._touchStartHandler + const touchEndHandler = container._touchEndHandler + + if (touchStartHandler) { + container.removeEventListener('touchstart', touchStartHandler) + delete container._touchStartHandler + } + + if (touchEndHandler) { + container.removeEventListener('touchend', touchEndHandler) + delete container._touchEndHandler + } + + // 移除删除按钮事件监听器 + const deleteBtn = container.querySelector('.image-delete-btn') + if (deleteBtn && deleteBtn._deleteHandler) { + deleteBtn.removeEventListener('click', deleteBtn._deleteHandler) + delete deleteBtn._deleteHandler + } + }, +} + // 初始化编辑器内容 onMounted(() => { - console.log('Editor mounted') if (editorRef.value) { - console.log('Editor ref available') if (props.modelValue) { - console.log('Setting initial content') try { editorRef.value.innerHTML = props.modelValue content.value = props.modelValue - console.log('Initial content set successfully') // 调整已有图片的高度 adjustExistingImages() } catch (error) { @@ -64,123 +153,40 @@ onMounted(() => { } else { // 即使没有初始内容,也要确保编辑器是可编辑的 editorRef.value.contentEditable = true - console.log('Editor initialized without initial content') } } // 记录初始视口高度 initialViewportHeight.value = window.visualViewport?.height || window.innerHeight - console.log('Initial viewport height:', initialViewportHeight.value) // 初始化CSS变量 document.documentElement.style.setProperty('--viewport-height', `${initialViewportHeight.value}px`) - console.log('Set viewport height CSS variable') // 添加虚拟键盘检测事件监听器 if (window.visualViewport) { - console.log('Adding viewport resize listener') window.visualViewport.addEventListener('resize', handleViewportResize) } else { - console.log('Adding window resize listener') window.addEventListener('resize', handleWindowResize) } // 为已有图片添加拖拽事件监听器 setTimeout(() => { - console.log('Adding drag event listeners to existing images') const imageContainers = editorRef.value.querySelectorAll('.image-container') - console.log('Found existing image containers:', imageContainers.length) imageContainers.forEach(container => { const img = container.querySelector('img.editor-image') if (!img) return - console.log('Adding touch listeners to image:', img) - // 添加触摸事件监听器 - container.addEventListener('touchstart', handleTouchStart) - container.addEventListener('touchmove', handleTouchMove) - container.addEventListener('touchend', handleTouchEnd) - container.addEventListener('touchcancel', handleTouchCancel) - // 为删除按钮添加点击事件 let deleteBtn = container.querySelector('.image-delete-btn') if (!deleteBtn) { // 如果删除按钮不存在,创建它 - console.log('Delete button not found in mounted hook, creating new one') deleteBtn = document.createElement('div') deleteBtn.className = 'image-delete-btn' container.appendChild(deleteBtn) } - if (deleteBtn) { - // 先移除可能已有的事件监听器,避免重复 - deleteBtn.removeEventListener('click', null) - - // 添加延时检查,确保删除按钮是可见的才执行删除操作 - setTimeout(() => { - deleteBtn.addEventListener('click', function (e) { - e.stopPropagation() - if (deleteBtn.classList.contains('visible')) { - container.remove() - handleInput() - } - }) - }, DELETE_BUTTON_DELAY) // 使用全局常量延时,确保删除按钮状态已正确设置 - } - - // 为图片容器添加短按事件以显示/隐藏删除按钮 - // 先移除可能已有的事件监听器,避免重复 - const touchStartHandler = container._touchStartHandler - const touchEndHandler = container._touchEndHandler - - if (touchStartHandler) { - container.removeEventListener('touchstart', touchStartHandler) - } - - if (touchEndHandler) { - container.removeEventListener('touchend', touchEndHandler) - } - - let touchStartTime = 0 - const newTouchStartHandler = function (e) { - touchStartTime = Date.now() - } - - const newTouchEndHandler = function (e) { - const touchDuration = Date.now() - touchStartTime - console.log('Touch end event triggered in mounted hook, duration:', touchDuration, 'isLongPress:', dragState.value.isLongPress) - // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 - if (touchDuration < 200 && !dragState.value.isLongPress) { - e.stopPropagation() - console.log('Short tap detected in mounted hook, toggling delete button visibility') - // 切换删除按钮的显示状态 - if (deleteBtn) { - console.log('Current delete button display style in mounted hook:', deleteBtn.style.display) - // 检查删除按钮当前是否可见 - const isCurrentlyVisible = deleteBtn.classList.contains('visible') - - console.log('Delete button current styles in mounted hook - inline:', deleteBtn.style.display, 'computed:', computedStyle.display) - - if (isCurrentlyVisible) { - deleteBtn.classList.remove('visible') - console.log('Delete button hidden in mounted hook') - } else { - deleteBtn.classList.add('visible') - console.log('Delete button displayed in mounted hook') - } - } else { - console.log('Delete button not found in mounted hook') - } - } else { - console.log('Not a short tap or isLongPress is true in mounted hook') - } - } - - container.addEventListener('touchstart', newTouchStartHandler) - container.addEventListener('touchend', newTouchEndHandler) - - // 保存事件处理函数的引用,以便后续移除 - container._touchStartHandler = newTouchStartHandler - container._touchEndHandler = newTouchEndHandler + // 使用事件管理器添加事件监听器 + eventManager.addImageContainerListeners(container, deleteBtn) }) }, 0) }) @@ -197,32 +203,7 @@ onUnmounted(() => { if (editorRef.value) { const imageContainers = editorRef.value.querySelectorAll('.image-container') imageContainers.forEach(container => { - // 移除拖拽事件监听器 - container.removeEventListener('touchstart', handleTouchStart) - container.removeEventListener('touchmove', handleTouchMove) - container.removeEventListener('touchend', handleTouchEnd) - container.removeEventListener('touchcancel', handleTouchCancel) - - // 移除短按事件监听器 - const touchStartHandler = container._touchStartHandler - const touchEndHandler = container._touchEndHandler - - if (touchStartHandler) { - container.removeEventListener('touchstart', touchStartHandler) - delete container._touchStartHandler - } - - if (touchEndHandler) { - container.removeEventListener('touchend', touchEndHandler) - delete container._touchEndHandler - } - - // 移除删除按钮事件监听器 - const deleteBtn = container.querySelector('.image-delete-btn') - if (deleteBtn) { - deleteBtn.removeEventListener('click', null) - deleteBtn.removeEventListener('touchend', null) - } + eventManager.removeImageContainerListeners(container) }) } }) @@ -476,6 +457,72 @@ const insertQuote = () => { } } +// 统一的待办事项创建函数 +const createTodoItem = (text = '') => { + // 创建待办事项容器 + const todoContainer = document.createElement('div') + todoContainer.contentEditable = false // 容器本身不可编辑 + todoContainer.className = 'todo-container' + + // 创建图标元素(复选框) + const icon = document.createElement('img') + icon.className = 'todo-icon' + icon.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' // 未完成状态图标 + icon.alt = '待办事项' + + // 创建内容容器(可编辑区域) + const contentSpan = document.createElement('div') + contentSpan.contentEditable = true // 内容区域可编辑 + contentSpan.className = 'todo-content' + contentSpan.textContent = text || '待办事项' // 默认文本 + + // 组装元素:将图标和内容区域添加到容器中 + todoContainer.appendChild(icon) + todoContainer.appendChild(contentSpan) + + return { todoContainer, icon, contentSpan } +} + +// 为待办事项添加事件监听器 +const addTodoEventListeners = (icon, contentSpan, todoContainer) => { + // 添加事件监听器到图标,实现待办事项完成状态切换 + icon.addEventListener('click', function () { + // 根据当前状态切换图标和样式 + if (this.src.includes('rtf_icon_gtasks.png')) { + // 切换到完成状态 + this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks_light.png' // 完成状态图标 + contentSpan.style.color = 'var(--text-tertiary)' // 灰色文字 + contentSpan.style.textDecoration = 'line-through' // 添加删除线 + } else { + // 切换到未完成状态 + this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' // 未完成状态图标 + contentSpan.style.color = 'var(--note-content)' // 正常文字颜色 + contentSpan.style.textDecoration = 'none' // 移除删除线 + } + handleInput() // 触发内容更新 + }) + + // 添加事件监听器到内容区域,监听内容变化和按键事件 + const checkContent = () => { + // 延迟检查,确保内容已更新 + setTimeout(() => { + if (contentSpan.textContent.trim() === '') { + // 如果内容为空,移除整个待办事项容器 + todoContainer.remove() + handleInput() + } + }, 0) + } + + contentSpan.addEventListener('input', checkContent) // 内容输入时检查 + contentSpan.addEventListener('blur', checkContent) // 失去焦点时检查 + + // 添加焦点事件监听器,确保工具栏在待办事项获得焦点时保持可见 + contentSpan.addEventListener('focus', () => { + isToolbarVisible.value = true + }) +} + // 插入待办事项列表 // 创建一个可交互的待办事项元素,包含复选框图标和可编辑内容区域 const insertTodoList = () => { @@ -486,26 +533,8 @@ const insertTodoList = () => { // 检查嵌套限制,防止在列表或引用中插入待办事项 if (isInListOrQuote()) return - // 创建待办事项容器 - const todoContainer = document.createElement('div') - todoContainer.contentEditable = false // 容器本身不可编辑 - todoContainer.className = 'todo-container' - - // 创建图标元素(复选框) - const icon = document.createElement('img') - icon.className = 'todo-icon' - icon.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' // 未完成状态图标 - icon.alt = '待办事项' - - // 创建内容容器(可编辑区域) - const contentSpan = document.createElement('div') - contentSpan.contentEditable = true // 内容区域可编辑 - contentSpan.className = 'todo-content' - contentSpan.textContent = '待办事项' // 默认文本 - - // 组装元素:将图标和内容区域添加到容器中 - todoContainer.appendChild(icon) - todoContainer.appendChild(contentSpan) + // 创建待办事项 + const { todoContainer, icon, contentSpan } = createTodoItem() // 插入到当前光标位置 range.insertNode(todoContainer) @@ -514,6 +543,9 @@ const insertTodoList = () => { const br = document.createElement('br') todoContainer.parentNode.insertBefore(br, todoContainer.nextSibling) + // 为待办事项添加事件监听器 + addTodoEventListeners(icon, contentSpan, todoContainer) + // 聚焦到内容区域(延迟执行,确保在handleToolClick处理完后再聚焦) setTimeout(() => { if (editorRef.value) { @@ -533,68 +565,13 @@ const insertTodoList = () => { } }, 0) - // 添加事件监听器到图标,实现待办事项完成状态切换 - icon.addEventListener('click', function () { - // 根据当前状态切换图标和样式 - if (this.src.includes('rtf_icon_gtasks.png')) { - // 切换到完成状态 - this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks_light.png' // 完成状态图标 - contentSpan.style.color = 'var(--text-tertiary)' // 灰色文字 - contentSpan.style.textDecoration = 'line-through' // 添加删除线 - } else { - // 切换到未完成状态 - this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' // 未完成状态图标 - contentSpan.style.color = 'var(--note-content)' // 正常文字颜色 - contentSpan.style.textDecoration = 'none' // 移除删除线 - } - handleInput() // 触发内容更新 - }) - - // 添加事件监听器到内容区域,监听内容变化和按键事件 - const checkContent = () => { - // 延迟检查,确保内容已更新 - setTimeout(() => { - if (contentSpan.textContent.trim() === '') { - // 如果内容为空,移除整个待办事项容器 - todoContainer.remove() - handleInput() - } - }, 0) - } - - contentSpan.addEventListener('input', checkContent) // 内容输入时检查 - contentSpan.addEventListener('blur', checkContent) // 失去焦点时检查 - - // 添加焦点事件监听器,确保工具栏在待办事项获得焦点时保持可见 - contentSpan.addEventListener('focus', () => { - isToolbarVisible.value = true - }) - // 监听回车键,创建同级待办事项 contentSpan.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault() // 创建新的待办事项 - const newTodoContainer = document.createElement('div') - newTodoContainer.contentEditable = false - newTodoContainer.className = 'todo-container' - - // 创建图标元素 - const newIcon = document.createElement('img') - newIcon.className = 'todo-icon' - newIcon.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' - newIcon.alt = '待办事项' - - // 创建内容容器 - const newContentSpan = document.createElement('div') - newContentSpan.contentEditable = true - newContentSpan.className = 'todo-content' - newContentSpan.textContent = '' - - // 组装元素 - newTodoContainer.appendChild(newIcon) - newTodoContainer.appendChild(newContentSpan) + const { todoContainer: newTodoContainer, icon: newIcon, contentSpan: newContentSpan } = createTodoItem() // 插入到当前待办事项后面 todoContainer.parentNode.insertBefore(newTodoContainer, todoContainer.nextSibling) @@ -603,6 +580,9 @@ const insertTodoList = () => { const newBr = document.createElement('br') newTodoContainer.parentNode.insertBefore(newBr, newTodoContainer.nextSibling) + // 为新待办事项添加事件监听器 + addTodoEventListeners(newIcon, newContentSpan, newTodoContainer) + // 聚焦到新内容区域 const newRange = document.createRange() newRange.selectNodeContents(newContentSpan) @@ -610,116 +590,6 @@ const insertTodoList = () => { selection.removeAllRanges() selection.addRange(newRange) - // 添加事件监听器到新图标 - newIcon.addEventListener('click', function () { - // 根据当前状态切换图标 - if (this.src.includes('rtf_icon_gtasks.png')) { - this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks_light.png' - newContentSpan.style.color = 'var(--text-tertiary)' - newContentSpan.style.textDecoration = 'line-through' - } else { - this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' - newContentSpan.style.color = 'var(--note-content)' - newContentSpan.style.textDecoration = 'none' - } - handleInput() - }) - - // 添加事件监听器到新内容区域 - const newCheckContent = () => { - setTimeout(() => { - if (newContentSpan.textContent.trim() === '') { - newTodoContainer.remove() - handleInput() - } - }, 0) - } - - newContentSpan.addEventListener('input', newCheckContent) - newContentSpan.addEventListener('blur', newCheckContent) - - // 添加焦点事件监听器,确保工具栏在待办事项获得焦点时保持可见 - newContentSpan.addEventListener('focus', () => { - isToolbarVisible.value = true - }) - - // 监听新内容区域的回车键 - newContentSpan.addEventListener('keydown', e => { - if (e.key === 'Enter') { - e.preventDefault() - - // 创建新的待办事项 - const nextTodoContainer = document.createElement('div') - nextTodoContainer.contentEditable = false - nextTodoContainer.className = 'todo-container' - - // 创建图标元素 - const nextIcon = document.createElement('img') - nextIcon.className = 'todo-icon' - nextIcon.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' - nextIcon.alt = '待办事项' - - // 创建内容容器 - const nextContentSpan = document.createElement('div') - nextContentSpan.contentEditable = true - nextContentSpan.className = 'todo-content' - nextContentSpan.textContent = '' - - // 组装元素 - nextTodoContainer.appendChild(nextIcon) - nextTodoContainer.appendChild(nextContentSpan) - - // 插入到当前待办事项后面 - newTodoContainer.parentNode.insertBefore(nextTodoContainer, newTodoContainer.nextSibling) - - // 添加换行 - const nextBr = document.createElement('br') - nextTodoContainer.parentNode.insertBefore(nextBr, nextTodoContainer.nextSibling) - - // 聚焦到新内容区域 - const nextRange = document.createRange() - nextRange.selectNodeContents(nextContentSpan) - nextRange.collapse(false) - selection.removeAllRanges() - selection.addRange(nextRange) - - // 添加事件监听器到新图标 - nextIcon.addEventListener('click', function () { - // 根据当前状态切换图标 - if (this.src.includes('rtf_icon_gtasks.png')) { - this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks_light.png' - nextContentSpan.style.color = 'var(--text-tertiary)' - nextContentSpan.style.textDecoration = 'line-through' - } else { - this.src = '/assets/icons/drawable-xxhdpi/rtf_icon_gtasks.png' - nextContentSpan.style.color = 'var(--note-content)' - nextContentSpan.style.textDecoration = 'none' - } - handleInput() - }) - - // 添加事件监听器到新内容区域 - const nextCheckContent = () => { - setTimeout(() => { - if (nextContentSpan.textContent.trim() === '') { - nextTodoContainer.remove() - handleInput() - } - }, 0) - } - - nextContentSpan.addEventListener('input', nextCheckContent) - nextContentSpan.addEventListener('blur', nextCheckContent) - - // 添加焦点事件监听器,确保工具栏在待办事项获得焦点时保持可见 - nextContentSpan.addEventListener('focus', () => { - isToolbarVisible.value = true - }) - - handleInput() - } - }) - handleInput() } }) @@ -769,9 +639,93 @@ const resetDragState = () => { } } +// 统一的图片容器创建函数 +const createImageContainer = imageDataUrl => { + // 创建图片容器 + const imgContainer = document.createElement('div') + imgContainer.className = 'image-container' + imgContainer.style.position = 'relative' + imgContainer.style.display = 'inline-block' + + // 创建图片元素 + const img = document.createElement('img') + img.src = imageDataUrl + img.className = 'editor-image' + img.setAttribute('data-draggable', 'true') + img.style.maxWidth = '100%' + img.style.height = 'auto' + img.style.display = 'block' + img.style.objectFit = 'cover' + img.style.boxSizing = 'border-box' + img.style.border = '0.625rem solid white' + img.style.borderRadius = '0.2rem' + img.style.boxShadow = '0 1px 5px rgba(0, 0, 0, 0.18)' + img.style.background = 'var(--background-secondary)' + img.style.position = 'relative' + img.style.outline = 'none' // 移除默认焦点轮廓 + img.style.userSelect = 'none' // 防止选中 + img.style.webkitUserSelect = 'none' // 防止选中 + img.style.mozUserSelect = 'none' // 防止选中 + img.style.msUserSelect = 'none' // 防止选中 + img.style.webkitTouchCallout = 'none' // 防止长按弹出菜单 + img.style.webkitTapHighlightColor = 'transparent' // 防止点击高亮 + img.draggable = true + + // 创建删除按钮 + const deleteBtn = document.createElement('div') + deleteBtn.className = 'image-delete-btn' + + // 将图片和删除按钮添加到容器中 + imgContainer.appendChild(img) + imgContainer.appendChild(deleteBtn) + + // 使用事件管理器添加事件监听器 + eventManager.addImageContainerListeners(imgContainer, deleteBtn) + + return { imgContainer, img, deleteBtn } +} + +// 调整图片尺寸的函数 +const adjustImageSize = img => { + // 创建一个临时图片来获取原始尺寸 + const tempImg = new Image() + tempImg.onload = function () { + // 获取CSS变量 + const editorFontSize = getComputedStyle(document.documentElement).getPropertyValue('--editor-font-size').trim() || '16px' + const editorLineHeight = getComputedStyle(document.documentElement).getPropertyValue('--editor-line-height').trim() || '1.6' + const fontSize = parseInt(editorFontSize) + const lineHeight = parseFloat(editorLineHeight) + + // 计算行高 + const computedLineHeight = fontSize * lineHeight + + // 获取编辑器的宽度(减去一些内边距) + const editorWidth = editorRef.value.offsetWidth - 20 // 20px为左右内边距 + + // 按宽度撑满计算调整后的尺寸 + const originalHeight = tempImg.height + const originalWidth = tempImg.width + const scaleRatio = editorWidth / originalWidth + const scaledHeight = originalHeight * scaleRatio + + // 计算调整后的高度,使其为行高的整数倍 + const scaleFactor = Math.max(1, Math.round(scaledHeight / computedLineHeight)) + const adjustedHeight = scaleFactor * computedLineHeight + + // 按比例调整宽度 + const adjustedWidth = (originalWidth * adjustedHeight) / originalHeight + + img.style.height = `${adjustedHeight}px` + img.style.width = `${adjustedWidth}px` + + // 确保图片与基准线对齐 + img.style.verticalAlign = 'top' + } + tempImg.src = img.src +} + // 插入图片 const insertImage = () => { - console.log('Inserting image') // 创建文件输入元素 const fileInput = document.createElement('input') fileInput.type = 'file' @@ -783,23 +737,18 @@ const insertImage = () => { // 监听文件选择事件 fileInput.addEventListener('change', function (event) { - console.log('File selected:', event.target.files) const file = event.target.files[0] if (file && file.type.startsWith('image/')) { - console.log('Image file selected') // 创建FileReader读取文件 const reader = new FileReader() reader.onload = function (e) { - console.log('File read successfully') // 获取图片数据URL const imageDataUrl = e.target.result - console.log('Image data URL:', imageDataUrl) // 获取当前选区 const selection = window.getSelection() if (selection.rangeCount > 0) { let range = selection.getRangeAt(0) - console.log('Current range:', range) // 检查选区是否在图片容器内部,如果是则调整到容器后面 const startContainer = range.startContainer @@ -827,174 +776,23 @@ const insertImage = () => { // 如果选区在图片容器内部,调整到容器后面 if (imageContainer) { - console.log('Selection is inside image container, adjusting range') range = document.createRange() range.setStartAfter(imageContainer) range.collapse(true) } // 创建图片容器 - const imgContainer = document.createElement('div') - imgContainer.className = 'image-container' - imgContainer.style.position = 'relative' - imgContainer.style.display = 'inline-block' + const { imgContainer, img, deleteBtn } = createImageContainer(imageDataUrl) - // 创建图片元素 - const img = document.createElement('img') - img.src = imageDataUrl - img.className = 'editor-image' - img.setAttribute('data-draggable', 'true') - img.style.maxWidth = '100%' - img.style.height = 'auto' - img.style.display = 'block' - img.style.objectFit = 'cover' - img.style.boxSizing = 'border-box' - img.style.border = '0.625rem solid white' - img.style.borderRadius = '0.2rem' - img.style.boxShadow = '0 1px 5px rgba(0, 0, 0, 0.18)' - img.style.background = 'var(--background-secondary)' - img.style.position = 'relative' - img.style.outline = 'none' // 移除默认焦点轮廓 - img.style.userSelect = 'none' // 防止选中 - img.style.webkitUserSelect = 'none' // 防止选中 - img.style.mozUserSelect = 'none' // 防止选中 - img.style.msUserSelect = 'none' // 防止选中 - img.style.webkitTouchCallout = 'none' // 防止长按弹出菜单 - img.style.webkitTapHighlightColor = 'transparent' // 防止点击高亮 - img.draggable = true - - // 创建删除按钮 - const deleteBtn = document.createElement('div') - deleteBtn.className = 'image-delete-btn' - - // 将图片和删除按钮添加到容器中 - imgContainer.appendChild(img) - imgContainer.appendChild(deleteBtn) - - console.log('Created image element:', img) - - // 创建一个临时图片来获取原始尺寸 - const tempImg = new Image() - tempImg.onload = function () { - console.log('Temp image loaded') - // 获取CSS变量 - const editorFontSize = getComputedStyle(document.documentElement).getPropertyValue('--editor-font-size').trim() || '16px' - const editorLineHeight = getComputedStyle(document.documentElement).getPropertyValue('--editor-line-height').trim() || '1.6' - const fontSize = parseInt(editorFontSize) - const lineHeight = parseFloat(editorLineHeight) - - // 计算行高 - const computedLineHeight = fontSize * lineHeight - - // 获取编辑器的宽度(减去一些内边距) - const editorWidth = editorRef.value.offsetWidth - 20 // 20px为左右内边距 - - // 按宽度撑满计算调整后的尺寸 - const originalHeight = tempImg.height - const originalWidth = tempImg.width - const scaleRatio = editorWidth / originalWidth - const scaledHeight = originalHeight * scaleRatio - - // 计算调整后的高度,使其为行高的整数倍 - const scaleFactor = Math.max(1, Math.round(scaledHeight / computedLineHeight)) - const adjustedHeight = scaleFactor * computedLineHeight - - // 按比例调整宽度 - const adjustedWidth = (originalWidth * adjustedHeight) / originalHeight - - img.style.height = `${adjustedHeight}px` - img.style.width = `${adjustedWidth}px` - - // 确保图片与基准线对齐 - img.style.verticalAlign = 'top' - } - tempImg.src = imageDataUrl - - // 添加触摸事件监听器实现拖拽功能 - // 先移除可能已有的事件监听器,避免重复 - imgContainer.removeEventListener('touchstart', handleTouchStart) - imgContainer.removeEventListener('touchmove', handleTouchMove) - imgContainer.removeEventListener('touchend', handleTouchEnd) - imgContainer.removeEventListener('touchcancel', handleTouchCancel) - - // 重新添加事件监听器 - imgContainer.addEventListener('touchstart', handleTouchStart) - imgContainer.addEventListener('touchmove', handleTouchMove) - imgContainer.addEventListener('touchend', handleTouchEnd) - imgContainer.addEventListener('touchcancel', handleTouchCancel) - - // 为删除按钮添加点击事件(鼠标和触摸) - // 先移除可能已有的事件监听器,避免重复 - deleteBtn.removeEventListener('click', null) - - // 添加延时检查,确保删除按钮是可见的才执行删除操作 - setTimeout(() => { - deleteBtn.addEventListener('click', function (e) { - e.stopPropagation() - if (deleteBtn.classList.contains('visible')) { - imgContainer.remove() - handleInput() - } - }) - }, DELETE_BUTTON_DELAY) // 使用全局常量延时,确保删除按钮状态已正确设置 - - // 为图片容器添加短按事件以显示/隐藏删除按钮 - let touchStartTime = 0 - const touchStartHandler = function (e) { - touchStartTime = Date.now() - } - - const touchEndHandler = function (e) { - const touchDuration = Date.now() - touchStartTime - console.log('Touch end event triggered, duration:', touchDuration, 'isLongPress:', dragState.value.isLongPress) - // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 - if (touchDuration < 200 && !dragState.value.isLongPress) { - e.stopPropagation() - console.log('Short tap detected, toggling delete button visibility') - // 切换删除按钮的显示状态 - if (deleteBtn) { - console.log('Current delete button display style:', deleteBtn.style.display) - // 检查删除按钮当前是否可见 - const isCurrentlyVisible = deleteBtn.classList.contains('visible') - - console.log('Delete button current styles - inline:', deleteBtn.style.display) - - if (isCurrentlyVisible) { - deleteBtn.classList.remove('visible') - console.log('Delete button hidden') - } else { - deleteBtn.classList.add('visible') - console.log('Delete button displayed') - } - } else { - console.log('Delete button not found') - } - } else { - console.log('Not a short tap or isLongPress is true') - } - } - - imgContainer.addEventListener('touchstart', touchStartHandler) - imgContainer.addEventListener('touchend', touchEndHandler) - - // 保存事件处理函数的引用,以便后续移除 - imgContainer._touchStartHandler = touchStartHandler - imgContainer._touchEndHandler = touchEndHandler - - console.log('Added touch event listeners') + // 调整图片尺寸 + adjustImageSize(img) // 插入图片容器到当前光标位置 range.insertNode(imgContainer) - console.log('Inserted image container into editor') - - // 调试信息 - console.log('Image container inserted:', imgContainer) - console.log('Next sibling (should be drag handle):', imgContainer.nextSibling) // 添加换行 const br = document.createElement('br') imgContainer.parentNode.insertBefore(br, imgContainer.nextSibling) - console.log('Added line break after image container') // 修正选区位置,避免嵌套插入 // 使用setTimeout确保DOM更新完成后再设置选区 @@ -1009,13 +807,11 @@ const insertImage = () => { // 重新聚焦到编辑器 if (editorRef.value) { editorRef.value.focus() - console.log('Focused editor') } }, 0) // 触发输入事件更新内容 handleInput() - console.log('Handled input event') } } reader.readAsDataURL(file) @@ -1023,12 +819,10 @@ const insertImage = () => { // 清理文件输入元素 document.body.removeChild(fileInput) - console.log('Removed file input') }) // 触发文件选择对话框 fileInput.click() - console.log('Clicked file input') } // 处理键盘事件 @@ -1186,8 +980,6 @@ const handleTouchMove = e => { if (!dragState.value.isLongPress || !img) return - e.preventDefault() // 阻止页面滚动 - dragState.value.currentY = currentY // 计算位移 @@ -1201,10 +993,6 @@ const handleTouchMove = e => { }) // 使用节流优化,避免过于频繁的检查 - if (!dragState.value.lastMoveTime) { - dragState.value.lastMoveTime = 0 - } - const now = Date.now() // 限制检查频率为每16ms一次(约60fps),提高响应速度 if (now - dragState.value.lastMoveTime >= 16) { @@ -1213,6 +1001,35 @@ const handleTouchMove = e => { } } +// 重置拖拽动画 +const resetDragAnimation = img => { + // 添加释放动画 + img.style.transition = 'all 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94)' + img.style.transform = 'translateY(0) scale(1)' + img.style.opacity = '1' + + // 移除拖拽指示器 + if (dragState.value.indicator) { + const indicator = dragState.value.indicator + indicator.style.transition = 'opacity 0.1s ease-out' + indicator.style.opacity = '0' + setTimeout(() => { + if (indicator.parentNode) { + indicator.parentNode.removeChild(indicator) + } + }, 100) + } + + // 延迟重置样式以显示动画 + setTimeout(() => { + if (img) { + img.classList.remove('dragging') + img.style.zIndex = '' + img.style.transition = '' + } + }, 150) +} + // 处理触摸结束事件 const handleTouchEnd = e => { // 清除长按定时器 @@ -1229,37 +1046,14 @@ const handleTouchEnd = e => { // 重置拖拽状态 const img = dragState.value.draggedImage - // 添加更流畅的释放动画 - img.style.transition = 'all 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94)' - img.style.transform = 'translateY(0) scale(1)' - img.style.opacity = '1' - - // 移除拖拽指示器 - if (dragState.value.indicator) { - const indicator = dragState.value.indicator - indicator.style.transition = 'opacity 0.1s ease-out' - indicator.style.opacity = '0' - setTimeout(() => { - if (indicator.parentNode) { - indicator.parentNode.removeChild(indicator) - } - }, 100) - } + // 重置拖拽动画 + resetDragAnimation(img) // 添加震动反馈(如果设备支持) if (navigator.vibrate) { navigator.vibrate(8) } - // 延迟重置样式以显示动画 - setTimeout(() => { - if (img) { - img.classList.remove('dragging') - img.style.zIndex = '' - img.style.transition = '' - } - }, 150) - // 重置状态 dragState.value.isLongPress = false dragState.value.draggedImage = null @@ -1287,31 +1081,8 @@ const handleTouchCancel = e => { // 重置拖拽状态 const img = dragState.value.draggedImage - // 添加取消动画 - img.style.transition = 'all 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94)' - img.style.transform = 'translateY(0) scale(1)' - img.style.opacity = '1' - - // 移除拖拽指示器 - if (dragState.value.indicator) { - const indicator = dragState.value.indicator - indicator.style.transition = 'opacity 0.1s ease-out' - indicator.style.opacity = '0' - setTimeout(() => { - if (indicator.parentNode) { - indicator.parentNode.removeChild(indicator) - } - }, 100) - } - - // 延迟重置样式以显示动画 - setTimeout(() => { - if (img) { - img.classList.remove('dragging') - img.style.zIndex = '' - img.style.transition = '' - } - }, 150) + // 重置拖拽动画 + resetDragAnimation(img) // 重置状态 dragState.value.isLongPress = false @@ -1547,15 +1318,10 @@ const handleToolClick = (action, event) => { editorRef.value.focus() } }, 0) - } else { - // 对于待办事项,确保工具栏保持可见 - isToolbarVisible.value = true } - // 确保工具栏在虚拟键盘可见时保持显示 - if (isKeyboardVisible.value) { - isToolbarVisible.value = true - } + // 确保工具栏保持可见 + isToolbarVisible.value = true } // 保持工具栏可见 @@ -1597,265 +1363,60 @@ const wrapOrphanedImages = () => { // 查找所有没有被.image-container包装的图片 const images = editorRef.value.querySelectorAll('img:not(.editor-image)') - console.log('Found orphaned images:', images.length) images.forEach(img => { // 检查图片是否已经在.image-container中 if (img.closest('.image-container')) return - console.log('Wrapping orphaned image') - // 检查图片的父元素是否是.image-container,避免嵌套 if (img.parentNode && img.parentNode.classList && img.parentNode.classList.contains('image-container')) { - console.log('Image is already in image-container, checking for delete button') // 确保图片有正确的类名 img.className = 'editor-image' img.setAttribute('data-draggable', 'true') // 为已存在的图片容器添加删除按钮事件监听器 const imgContainer = img.parentNode - const deleteBtn = imgContainer.querySelector('.image-delete-btn') - if (deleteBtn) { - console.log('Found existing delete button, adding event listeners') - // 先移除可能已有的事件监听器,避免重复 - deleteBtn.removeEventListener('click', null) - - // 添加延时检查,确保删除按钮是可见的才执行删除操作 - setTimeout(() => { - deleteBtn.addEventListener('click', function (e) { - e.stopPropagation() - if (deleteBtn.classList.contains('visible')) { - imgContainer.remove() - handleInput() - } - }) - }, DELETE_BUTTON_DELAY) // 使用全局常量延时,确保删除按钮状态已正确设置 - - // 为图片容器添加短按事件以显示/隐藏删除按钮 - // 先移除可能已有的事件监听器,避免重复 - const touchStartHandler = imgContainer._touchStartHandler - const touchEndHandler = imgContainer._touchEndHandler - - if (touchStartHandler) { - imgContainer.removeEventListener('touchstart', touchStartHandler) - } - - if (touchEndHandler) { - imgContainer.removeEventListener('touchend', touchEndHandler) - } - - let touchStartTime = 0 - const newTouchStartHandler = function (e) { - touchStartTime = Date.now() - } - - const newTouchEndHandler = function (e) { - const touchDuration = Date.now() - touchStartTime - console.log('Touch end event triggered for existing image, duration:', touchDuration, 'isLongPress:', dragState.value.isLongPress) - // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 - if (touchDuration < 200 && !dragState.value.isLongPress) { - e.stopPropagation() - console.log('Short tap detected for existing image, toggling delete button visibility') - // 切换删除按钮的显示状态 - if (deleteBtn) { - console.log('Current delete button display style for existing image:', deleteBtn.style.display) - // 检查删除按钮当前是否可见 - const isCurrentlyVisible = deleteBtn.classList.contains('visible') - - console.log('Delete button current styles for existing image - inline:', deleteBtn.style.display) - - if (isCurrentlyVisible) { - deleteBtn.classList.remove('visible') - console.log('Delete button hidden for existing image') - } else { - deleteBtn.classList.add('visible') - console.log('Delete button displayed for existing image') - } - } else { - console.log('Delete button not found for existing image') - } - } else { - console.log('Not a short tap or isLongPress is true for existing image') - } - } - - imgContainer.addEventListener('touchstart', newTouchStartHandler) - imgContainer.addEventListener('touchend', newTouchEndHandler) - - // 保存事件处理函数的引用,以便后续移除 - imgContainer._touchStartHandler = newTouchStartHandler - imgContainer._touchEndHandler = newTouchEndHandler + let deleteBtn = imgContainer.querySelector('.image-delete-btn') + if (!deleteBtn) { + // 如果删除按钮不存在,创建它 + deleteBtn = document.createElement('div') + deleteBtn.className = 'image-delete-btn' + container.appendChild(deleteBtn) } + + // 使用事件管理器添加事件监听器 + eventManager.addImageContainerListeners(imgContainer, deleteBtn) return } // 创建图片容器 - const imgContainer = document.createElement('div') - imgContainer.className = 'image-container' - imgContainer.style.position = 'relative' - imgContainer.style.display = 'inline-block' - - // 设置图片样式 - img.className = 'editor-image' - img.setAttribute('data-draggable', 'true') - img.style.maxWidth = '100%' - img.style.height = 'auto' - img.style.display = 'block' - img.style.objectFit = 'cover' - img.style.boxSizing = 'border-box' - img.style.border = '0.625rem solid white' - img.style.borderRadius = '0.2rem' - img.style.boxShadow = '0 1px 5px rgba(0, 0, 0, 0.18)' - img.style.background = 'var(--background-secondary)' - img.style.position = 'relative' - img.style.outline = 'none' - img.style.userSelect = 'none' // 防止选中 - img.style.webkitUserSelect = 'none' // 防止选中 - img.style.mozUserSelect = 'none' // 防止选中 - img.style.msUserSelect = 'none' // 防止选中 - img.style.webkitTouchCallout = 'none' // 防止长按弹出菜单 - img.style.webkitTapHighlightColor = 'transparent' // 防止点击高亮 - img.draggable = true - - // 创建删除按钮 - const deleteBtn = document.createElement('div') - deleteBtn.className = 'image-delete-btn' - - // 将图片和删除按钮添加到容器中 - imgContainer.appendChild(img) - imgContainer.appendChild(deleteBtn) + const { imgContainer, img: editorImg, deleteBtn } = createImageContainer(img.src) // 替换原来的图片 img.parentNode.replaceChild(imgContainer, img) - // 为新包装的图片添加事件监听器 - // 先移除可能已有的事件监听器,避免重复 - imgContainer.removeEventListener('touchstart', handleTouchStart) - imgContainer.removeEventListener('touchmove', handleTouchMove) - imgContainer.removeEventListener('touchend', handleTouchEnd) - imgContainer.removeEventListener('touchcancel', handleTouchCancel) - - // 重新添加事件监听器 - imgContainer.addEventListener('touchstart', handleTouchStart) - imgContainer.addEventListener('touchmove', handleTouchMove) - imgContainer.addEventListener('touchend', handleTouchEnd) - imgContainer.addEventListener('touchcancel', handleTouchCancel) - - // 为删除按钮添加点击事件 - // 先移除可能已有的事件监听器,避免重复 - deleteBtn.removeEventListener('click', null) - - // 添加延时检查,确保删除按钮是可见的才执行删除操作 - setTimeout(() => { - deleteBtn.addEventListener('click', function (e) { - e.stopPropagation() - if (deleteBtn.classList.contains('visible')) { - imgContainer.remove() - handleInput() - } - }) - }, DELETE_BUTTON_DELAY) // 使用全局常量延时,确保删除按钮状态已正确设置 - - // 为图片容器添加短按事件以显示/隐藏删除按钮 - let touchStartTime = 0 - const touchStartHandler = function (e) { - touchStartTime = Date.now() - } - - const touchEndHandler = function (e) { - const touchDuration = Date.now() - touchStartTime - console.log('Touch end event triggered in wrapOrphanedImages, duration:', touchDuration, 'isLongPress:', dragState.value.isLongPress) - // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 - if (touchDuration < 200 && !dragState.value.isLongPress) { - e.stopPropagation() - console.log('Short tap detected in wrapOrphanedImages, toggling delete button visibility') - // 切换删除按钮的显示状态 - if (deleteBtn) { - console.log('Current delete button display style in wrapOrphanedImages:', deleteBtn.style.display) - // 检查删除按钮当前是否可见 - const isCurrentlyVisible = deleteBtn.classList.contains('visible') - - console.log('Delete button current styles - inline:', deleteBtn.style.display) - - if (isCurrentlyVisible) { - deleteBtn.classList.remove('visible') - console.log('Delete button hidden in wrapOrphanedImages') - } else { - deleteBtn.classList.add('visible') - console.log('Delete button displayed in wrapOrphanedImages') - } - } else { - console.log('Delete button not found in wrapOrphanedImages') - } - } else { - console.log('Not a short tap or isLongPress is true in wrapOrphanedImages') - } - } - - imgContainer.addEventListener('touchstart', touchStartHandler) - imgContainer.addEventListener('touchend', touchEndHandler) - - // 保存事件处理函数的引用,以便后续移除 - imgContainer._touchStartHandler = touchStartHandler - imgContainer._touchEndHandler = touchEndHandler + // 调整图片尺寸 + adjustImageSize(editorImg) }) } // 调整已有图片的高度 const adjustExistingImages = () => { - console.log('Adjusting existing images') // 等待DOM更新完成 setTimeout(() => { if (editorRef.value) { const imageContainers = editorRef.value.querySelectorAll('.image-container') - console.log('Found image containers:', imageContainers.length) imageContainers.forEach(container => { const img = container.querySelector('img.editor-image') if (!img) return - console.log('Processing image:', img) // 只处理还没有调整过高度的图片 if (!img.dataset.heightAdjusted) { - console.log('Adjusting height for image') - // 创建一个临时图片来获取原始尺寸 - const tempImg = new Image() - tempImg.onload = function () { - // 获取CSS变量 - const editorFontSize = getComputedStyle(document.documentElement).getPropertyValue('--editor-font-size').trim() || '16px' - const editorLineHeight = getComputedStyle(document.documentElement).getPropertyValue('--editor-line-height').trim() || '1.6' - const fontSize = parseInt(editorFontSize) - const lineHeight = parseFloat(editorLineHeight) + // 调整图片尺寸 + adjustImageSize(img) - // 计算行高 - const computedLineHeight = fontSize * lineHeight - - // 获取编辑器的宽度(减去一些内边距) - const editorWidth = editorRef.value.offsetWidth - 20 // 20px为左右内边距 - - // 按宽度撑满计算调整后的尺寸 - const originalHeight = tempImg.height - const originalWidth = tempImg.width - const scaleRatio = editorWidth / originalWidth - const scaledHeight = originalHeight * scaleRatio - - // 计算调整后的高度,使其为行高的整数倍 - const scaleFactor = Math.max(1, Math.round(scaledHeight / computedLineHeight)) - const adjustedHeight = scaleFactor * computedLineHeight - - // 按比例调整宽度 - const adjustedWidth = (originalWidth * adjustedHeight) / originalHeight - - img.style.height = `${adjustedHeight}px` - img.style.width = `${adjustedWidth}px` - - // 确保图片与基准线对齐 - img.style.verticalAlign = 'top' - - // 标记图片已调整过高度 - img.dataset.heightAdjusted = 'true' - console.log('Adjusted image dimensions:', adjustedWidth, adjustedHeight) - } - tempImg.src = img.src + // 标记图片已调整过高度 + img.dataset.heightAdjusted = 'true' } }) @@ -1864,103 +1425,19 @@ const adjustExistingImages = () => { const img = container.querySelector('img.editor-image') if (!img) return - console.log('Adding drag functionality to image:', img) - // 为图片容器添加事件监听器(总是添加,确保功能正常) - // 先移除可能已有的事件监听器,避免重复 - container.removeEventListener('touchstart', handleTouchStart) - container.removeEventListener('touchmove', handleTouchMove) - container.removeEventListener('touchend', handleTouchEnd) - container.removeEventListener('touchcancel', handleTouchCancel) - - // 重新添加事件监听器 - container.addEventListener('touchstart', handleTouchStart) - container.addEventListener('touchmove', handleTouchMove) - container.addEventListener('touchend', handleTouchEnd) - container.addEventListener('touchcancel', handleTouchCancel) - // 为删除按钮添加点击事件 let deleteBtn = container.querySelector('.image-delete-btn') if (!deleteBtn) { // 如果删除按钮不存在,创建它 - console.log('Delete button not found, creating new one') deleteBtn = document.createElement('div') deleteBtn.className = 'image-delete-btn' container.appendChild(deleteBtn) } - if (deleteBtn) { - // 先移除可能已有的事件监听器,避免重复 - deleteBtn.removeEventListener('click', null) - - // 添加延时检查,确保删除按钮是可见的才执行删除操作 - setTimeout(() => { - deleteBtn.addEventListener('click', function (e) { - e.stopPropagation() - if (deleteBtn.classList.contains('visible')) { - imgContainer.remove() - handleInput() - } - }) - }, DELETE_BUTTON_DELAY) // 使用全局常量延时,确保删除按钮状态已正确设置 - } - - // 为图片容器添加短按事件以显示/隐藏删除按钮 - // 先移除可能已有的事件监听器,避免重复 - const touchStartHandler = container._touchStartHandler - const touchEndHandler = container._touchEndHandler - - if (touchStartHandler) { - container.removeEventListener('touchstart', touchStartHandler) - } - - if (touchEndHandler) { - container.removeEventListener('touchend', touchEndHandler) - } - - let touchStartTime = 0 - const newTouchStartHandler = function (e) { - touchStartTime = Date.now() - } - - const newTouchEndHandler = function (e) { - const touchDuration = Date.now() - touchStartTime - console.log('Touch end event triggered in adjustExistingImages, duration:', touchDuration, 'isLongPress:', dragState.value.isLongPress) - // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 - if (touchDuration < 200 && !dragState.value.isLongPress) { - e.stopPropagation() - console.log('Short tap detected in adjustExistingImages, toggling delete button visibility') - // 切换删除按钮的显示状态 - if (deleteBtn) { - console.log('Current delete button display style in adjustExistingImages:', deleteBtn.style.display) - // 检查删除按钮当前是否可见 - const isCurrentlyVisible = deleteBtn.classList.contains('visible') - - console.log('Delete button current styles - inline:', deleteBtn.style.display) - - if (isCurrentlyVisible) { - deleteBtn.classList.remove('visible') - console.log('Delete button hidden in adjustExistingImages') - } else { - deleteBtn.classList.add('visible') - console.log('Delete button displayed in adjustExistingImages') - } - } else { - console.log('Delete button not found in adjustExistingImages') - } - } else { - console.log('Not a short tap or isLongPress is true in adjustExistingImages') - } - } - - container.addEventListener('touchstart', newTouchStartHandler) - container.addEventListener('touchend', newTouchEndHandler) - - // 保存事件处理函数的引用,以便后续移除 - container._touchStartHandler = newTouchStartHandler - container._touchEndHandler = newTouchEndHandler + // 使用事件管理器添加事件监听器 + eventManager.addImageContainerListeners(container, deleteBtn) img.setAttribute('data-touch-listeners', 'true') - console.log('Added touch event listeners') }) } }, 0) @@ -2011,12 +1488,10 @@ const cleanContentForSave = () => { defineExpose({ getContent: () => cleanContentForSave(), setContent: newContent => { - console.log('Setting content:', newContent) content.value = newContent || '' if (editorRef.value) { try { editorRef.value.innerHTML = content.value - console.log('Content set successfully in editorRef') // 重置拖拽状态,确保isLongPress为false dragState.value.isLongPress = false dragState.value.draggedImage = null @@ -2027,128 +1502,21 @@ defineExpose({ wrapOrphanedImages() // 调整已有图片的高度并添加拖拽功能 adjustExistingImages() - // 为图片添加拖拽事件监听器 - setTimeout(() => { - console.log('Adding drag event listeners to images in setContent') - const imageContainers = editorRef.value.querySelectorAll('.image-container') - console.log('Found image containers in setContent:', imageContainers.length) - imageContainers.forEach(container => { - const img = container.querySelector('img.editor-image') - if (!img) return - - console.log('Adding touch listeners to image in setContent:', img) - // 先移除可能已有的事件监听器,避免重复 - container.removeEventListener('touchstart', handleTouchStart) - container.removeEventListener('touchmove', handleTouchMove) - container.removeEventListener('touchend', handleTouchEnd) - container.removeEventListener('touchcancel', handleTouchCancel) - - // 重新添加事件监听器 - container.addEventListener('touchstart', handleTouchStart) - container.addEventListener('touchmove', handleTouchMove) - container.addEventListener('touchend', handleTouchEnd) - container.addEventListener('touchcancel', handleTouchCancel) - - // 为删除按钮添加点击事件 - const deleteBtn = container.querySelector('.image-delete-btn') - if (deleteBtn) { - // 先移除可能已有的事件监听器,避免重复 - deleteBtn.removeEventListener('click', null) - - // 添加延时检查,确保删除按钮是可见的才执行删除操作 - setTimeout(() => { - deleteBtn.addEventListener('click', function (e) { - e.stopPropagation() - if (deleteBtn.classList.contains('visible')) { - imgContainer.remove() - handleInput() - } - }) - }, DELETE_BUTTON_DELAY) // 使用全局常量延时,确保删除按钮状态已正确设置 - } - - // 为图片容器添加短按事件以显示/隐藏删除按钮 - // 先移除可能已有的事件监听器,避免重复 - const touchStartHandler = container._touchStartHandler - const touchEndHandler = container._touchEndHandler - - if (touchStartHandler) { - container.removeEventListener('touchstart', touchStartHandler) - } - - if (touchEndHandler) { - container.removeEventListener('touchend', touchEndHandler) - } - - let touchStartTime = 0 - const newTouchStartHandler = function (e) { - touchStartTime = Date.now() - } - - const newTouchEndHandler = function (e) { - const touchDuration = Date.now() - touchStartTime - console.log('Touch end event triggered in setContent, duration:', touchDuration, 'isLongPress:', dragState.value.isLongPress) - // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 - if (touchDuration < 200 && !dragState.value.isLongPress) { - e.stopPropagation() - console.log('Short tap detected in setContent, toggling delete button visibility') - // 切换删除按钮的显示状态 - if (deleteBtn) { - console.log('Current delete button display style in setContent:', deleteBtn.style.display) - // 检查删除按钮当前是否可见 - const computedStyle = getComputedStyle(deleteBtn) - const isCurrentlyVisible = deleteBtn.classList.contains('visible') - - console.log('Delete button current styles - inline:', deleteBtn.style.display, 'computed:', computedStyle.display) - console.log('Delete button background image:', computedStyle.backgroundImage) - console.log('Delete button width:', computedStyle.width, 'height:', computedStyle.height) - console.log('Delete button position:', computedStyle.position) - console.log('Delete button z-index:', computedStyle.zIndex) - - if (isCurrentlyVisible) { - deleteBtn.classList.remove('visible') - console.log('Delete button hidden in setContent') - } else { - deleteBtn.classList.add('visible') - console.log('Delete button displayed in setContent') - } - } else { - console.log('Delete button not found in setContent') - } - } else { - console.log('Not a short tap or isLongPress is true in setContent') - } - } - - container.addEventListener('touchstart', newTouchStartHandler) - container.addEventListener('touchend', newTouchEndHandler) - - // 保存事件处理函数的引用,以便后续移除 - container._touchStartHandler = newTouchStartHandler - container._touchEndHandler = newTouchEndHandler - - img.setAttribute('data-touch-listeners', 'true') - console.log('Added touch event listeners') - }) - }, 0) } catch (error) { console.error('Failed to set innerHTML:', error) // 备选方案:使用textContent try { editorRef.value.textContent = content.value - console.log('Content set using textContent') } catch (textContentError) { console.error('Failed to set textContent:', textContentError) } } } else { // 如果editorRef还不可用,延迟设置 - console.log('Editor ref is not available, will retry when mounted') setTimeout(() => { if (editorRef.value) { try { editorRef.value.innerHTML = content.value - console.log('Content set successfully after delay') // 重置拖拽状态,确保isLongPress为false dragState.value.isLongPress = false dragState.value.draggedImage = null @@ -2159,105 +1527,6 @@ defineExpose({ wrapOrphanedImages() // 调整已有图片的高度并添加拖拽功能 adjustExistingImages() - // 为图片添加拖拽事件监听器 - setTimeout(() => { - console.log('Adding drag event listeners to images in delayed setContent') - const imageContainers = editorRef.value.querySelectorAll('.image-container') - console.log('Found image containers in delayed setContent:', imageContainers.length) - imageContainers.forEach(container => { - const img = container.querySelector('img.editor-image') - if (!img) return - - console.log('Adding touch listeners to image in delayed setContent:', img) - // 先移除可能已有的事件监听器,避免重复 - container.removeEventListener('touchstart', handleTouchStart) - container.removeEventListener('touchmove', handleTouchMove) - container.removeEventListener('touchend', handleTouchEnd) - container.removeEventListener('touchcancel', handleTouchCancel) - - // 重新添加事件监听器 - container.addEventListener('touchstart', handleTouchStart) - container.addEventListener('touchmove', handleTouchMove) - container.addEventListener('touchend', handleTouchEnd) - container.addEventListener('touchcancel', handleTouchCancel) - - // 为删除按钮添加点击事件 - const deleteBtn = container.querySelector('.image-delete-btn') - if (deleteBtn) { - // 先移除可能已有的事件监听器,避免重复 - deleteBtn.removeEventListener('click', null) - - // 添加延时检查,确保删除按钮是可见的才执行删除操作 - setTimeout(() => { - deleteBtn.addEventListener('click', function (e) { - e.stopPropagation() - if (deleteBtn.classList.contains('visible')) { - imgContainer.remove() - handleInput() - } - }) - }, DELETE_BUTTON_DELAY) // 使用全局常量延时,确保删除按钮状态已正确设置 - } - - // 为图片容器添加短按事件以显示/隐藏删除按钮 - // 先移除可能已有的事件监听器,避免重复 - const touchStartHandler = container._touchStartHandler - const touchEndHandler = container._touchEndHandler - - if (touchStartHandler) { - container.removeEventListener('touchstart', touchStartHandler) - } - - if (touchEndHandler) { - container.removeEventListener('touchend', touchEndHandler) - } - - let touchStartTime = 0 - const newTouchStartHandler = function (e) { - touchStartTime = Date.now() - } - - const newTouchEndHandler = function (e) { - const touchDuration = Date.now() - touchStartTime - console.log('Touch end event triggered in delayed setContent, duration:', touchDuration, 'isLongPress:', dragState.value.isLongPress) - // 短按(小于200ms)且非长按拖拽状态时切换删除按钮显示 - if (touchDuration < 200 && !dragState.value.isLongPress) { - e.stopPropagation() - console.log('Short tap detected in delayed setContent, toggling delete按钮visibility') - // 切换删除按钮的显示状态 - if (deleteBtn) { - console.log('Current delete button display style in delayed setContent:', deleteBtn.style.display) - // 检查删除按钮当前是否可见 - const isCurrentlyVisible = deleteBtn.classList.contains('visible') - - console.log('Delete button current styles - inline:', deleteBtn.style.display, 'computed:', computedStyle.display) - - if (isCurrentlyVisible) { - deleteBtn.classList.remove('visible') - console.log('Delete button hidden in delayed setContent') - } else { - deleteBtn.classList.add('visible') - console.log('Delete button displayed in delayed setContent') - } - } else { - console.log('Delete button not found in delayed setContent') - } - } else { - console.log('Not a short tap or isLongPress is true in delayed setContent') - } - } - - container.addEventListener('touchstart', newTouchStartHandler) - container.addEventListener('touchend', newTouchEndHandler) - - // 保存事件处理函数的引用,以便后续移除 - container._touchStartHandler = newTouchStartHandler - container._touchEndHandler = newTouchEndHandler - - img.setAttribute('data-touch-listeners', 'true') - console.log('Added touch event listeners') - }) - }, 0) } catch (error) { console.error('Failed to set innerHTML after delay:', error) } @@ -2518,13 +1787,11 @@ defineExpose({ background-color: transparent; /* 确保背景透明 */ pointer-events: none; opacity: 0; - display: none; } :deep(.editor-content .image-delete-btn.visible) { - pointer-events: all; + pointer-events: auto; opacity: 1; - display: block; } :deep(.editor-content .editor-image.draggable) {