diff --git a/src/components/NoteItem.vue b/src/components/NoteItem.vue index b0e9bbd..49800e9 100644 --- a/src/components/NoteItem.vue +++ b/src/components/NoteItem.vue @@ -115,16 +115,22 @@ const SLIDE_THRESHOLD = 64 // 4rem 转换为 px const handleContainerTouchStart = e => { // 阻止事件冒泡到父组件 e.stopPropagation() + // 阻止父级滚动容器的滚动行为 + e.stopImmediatePropagation() } const handleContainerTouchMove = e => { // 阻止事件冒泡到父组件 e.stopPropagation() + // 阻止父级滚动容器的滚动行为 + e.stopImmediatePropagation() } const handleContainerTouchEnd = e => { // 阻止事件冒泡到父组件 e.stopPropagation() + // 阻止父级滚动容器的滚动行为 + e.stopImmediatePropagation() } // 处理便签点击事件 @@ -168,6 +174,8 @@ const handleDelete = () => { const handleTouchStart = e => { // 阻止事件冒泡到父组件,防止页面滚动时触发便签滑动 e.stopPropagation() + // 阻止父级滚动容器的滚动行为 + e.stopImmediatePropagation() // 重置滑动状态 startX.value = e.touches[0].clientX startY.value = e.touches[0].clientY // 记录起始Y坐标 @@ -187,12 +195,18 @@ const handleTouchMove = e => { const diffY = currentY - startY.value // 如果已经确定是滚动,则不再处理 - if (isScrolling.value) return + if (isScrolling.value) { + // 阻止事件冒泡到父组件,防止页面滚动时触发便签滑动 + e.stopPropagation() + return + } // 判断是滚动还是滑动操作 // 如果Y轴移动距离大于X轴移动距离,则认为是滚动 if (Math.abs(diffY) > Math.abs(diffX)) { isScrolling.value = true + // 阻止事件冒泡到父组件,防止页面滚动时触发便签滑动 + e.stopPropagation() return } @@ -242,6 +256,8 @@ const handleTouchEnd = e => { // 阻止事件冒泡到父组件,防止页面滚动时触发便签滑动 e?.stopPropagation() + // 阻止父级滚动容器的滚动行为 + e?.stopImmediatePropagation() // 如果滑动超过阈值,保持滑出状态;否则回弹 if (slideOffset.value >= SLIDE_THRESHOLD) { diff --git a/src/components/RichTextEditor.vue b/src/components/RichTextEditor.vue index 91df2df..23074df 100644 --- a/src/components/RichTextEditor.vue +++ b/src/components/RichTextEditor.vue @@ -577,6 +577,66 @@ const insertTodoList = () => { } } +// 处理图片拖拽开始 +const handleImageDragStart = (e) => { + e.dataTransfer.effectAllowed = 'move' + e.target.classList.add('dragging') +} + +// 处理图片拖拽经过 +const handleImageDragOver = (e) => { + e.preventDefault() + e.dataTransfer.dropEffect = 'move' + + const target = e.target + const draggingElement = document.querySelector('img.editor-image.dragging') + + if (draggingElement && target !== draggingElement && target.classList.contains('editor-image')) { + const rect = target.getBoundingClientRect() + const midpoint = rect.top + rect.height / 2 + + if (e.clientY < midpoint) { + // 在目标元素上方插入 + target.parentNode.insertBefore(draggingElement, target) + // 同时移动拖拽手柄 + const targetHandle = target.nextElementSibling + const draggingHandle = draggingElement.nextElementSibling + if (targetHandle && targetHandle.classList.contains('image-drag-handle') && + draggingHandle && draggingHandle.classList.contains('image-drag-handle')) { + target.parentNode.insertBefore(draggingHandle, targetHandle) + } + } else { + // 在目标元素下方插入 + target.parentNode.insertBefore(draggingElement, target.nextSibling) + // 同时移动拖拽手柄 + const targetHandle = target.nextElementSibling + const draggingHandle = draggingElement.nextElementSibling + if (targetHandle && targetHandle.classList.contains('image-drag-handle') && + draggingHandle && draggingHandle.classList.contains('image-drag-handle')) { + target.parentNode.insertBefore(draggingHandle, targetHandle.nextSibling) + } + } + } +} + +// 处理图片拖拽释放 +const handleImageDrop = (e) => { + e.preventDefault() + const draggingElement = document.querySelector('img.editor-image.dragging') + if (draggingElement) { + draggingElement.classList.remove('dragging') + handleInput() // 触发内容更新 + } +} + +// 处理图片拖拽结束 +const handleImageDragEnd = (e) => { + const draggingElement = document.querySelector('img.editor-image.dragging') + if (draggingElement) { + draggingElement.classList.remove('dragging') + } +} + // 插入图片 const insertImage = () => { // 创建文件输入元素 @@ -607,7 +667,18 @@ const insertImage = () => { const img = document.createElement('img') img.src = imageDataUrl img.className = 'editor-image' - + 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' // 移除默认焦点轮廓 + // 创建一个临时图片来获取原始尺寸 const tempImg = new Image() tempImg.onload = function () { @@ -641,11 +712,109 @@ const insertImage = () => { // 确保图片与基准线对齐 img.style.verticalAlign = 'top' + + // 创建拖拽手柄 + const dragHandle = document.createElement('div') + dragHandle.className = 'image-drag-handle' + dragHandle.style.position = 'absolute' + dragHandle.style.top = '0.3125rem' + dragHandle.style.right = '0.3125rem' + dragHandle.style.width = '1.5rem' + dragHandle.style.height = '1.5rem' + dragHandle.style.backgroundImage = "url('/assets/icons/drawable-xxhdpi/detail_note_item_image_move.png')" + dragHandle.style.backgroundSize = 'contain' + dragHandle.style.backgroundRepeat = 'no-repeat' + dragHandle.style.cursor = 'move' + dragHandle.style.display = 'none' + dragHandle.style.zIndex = '1000' + dragHandle.style.pointerEvents = 'auto' + dragHandle.style.position = 'absolute' + + // 调试信息 + console.log('Creating drag handle for image') + + // 将拖拽手柄添加到图片后面(使用insertAdjacentElement) + img.insertAdjacentElement('afterend', dragHandle) } tempImg.src = imageDataUrl + // 添加事件监听器来显示/隐藏拖拽手柄 + img.addEventListener('focus', function() { + const dragHandle = img.nextElementSibling + if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { + dragHandle.classList.add('visible') + dragHandle.style.display = 'block' // 直接设置显示样式 + } + }) + + img.addEventListener('blur', function() { + const dragHandle = img.nextElementSibling + if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { + // 延迟隐藏,确保用户有时间将鼠标移到手柄上 + setTimeout(() => { + if (!dragHandle.matches(':hover')) { + dragHandle.classList.remove('visible') + dragHandle.style.display = 'none' // 直接设置隐藏样式 + } + }, 100) + } + }) + + img.addEventListener('mouseover', function() { + const dragHandle = img.nextElementSibling + if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { + dragHandle.classList.add('visible') + dragHandle.style.display = 'block' // 直接设置显示样式 + } + }) + + img.addEventListener('mouseout', function(e) { + const dragHandle = img.nextElementSibling + if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { + // 延迟隐藏,确保用户有时间将鼠标移到手柄上 + setTimeout(() => { + if (!dragHandle.matches(':hover') && document.activeElement !== img) { + dragHandle.classList.remove('visible') + dragHandle.style.display = 'none' // 直接设置隐藏样式 + } + }, 100) + } + }) + + // 添加拖拽手柄的事件监听器 + setTimeout(() => { + const dragHandle = img.nextElementSibling + if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { + dragHandle.addEventListener('mouseover', function() { + dragHandle.classList.add('visible') + dragHandle.style.display = 'block' // 直接设置显示样式 + }) + + dragHandle.addEventListener('mouseout', function(e) { + // 延迟隐藏,确保用户有时间将鼠标移到图片上 + setTimeout(() => { + if (!img.matches(':hover') && document.activeElement !== img) { + dragHandle.classList.remove('visible') + dragHandle.style.display = 'none' // 直接设置隐藏样式 + } + }, 100) + }) + } + }, 0) + + // 添加拖拽功能 + img.draggable = true + img.addEventListener('dragstart', handleImageDragStart) + img.addEventListener('dragover', handleImageDragOver) + img.addEventListener('drop', handleImageDrop) + img.addEventListener('dragend', handleImageDragEnd) + // 插入图片到当前光标位置 range.insertNode(img) + + // 调试信息 + console.log('Image inserted:', img) + console.log('Next sibling (should be drag handle):', img.nextSibling) // 添加换行 const br = document.createElement('br') @@ -941,6 +1110,99 @@ const adjustExistingImages = () => { tempImg.src = img.src } }) + + // 为现有图片添加拖拽功能 + images.forEach(img => { + // 确保图片有拖拽属性 + if (!img.hasAttribute('draggable')) { + img.draggable = true + + // 添加拖拽事件监听器 + img.addEventListener('dragstart', handleImageDragStart) + img.addEventListener('dragover', handleImageDragOver) + img.addEventListener('drop', handleImageDrop) + img.addEventListener('dragend', handleImageDragEnd) + } + + // 检查是否已存在拖拽手柄 + let existingHandle = img.nextElementSibling + if (!existingHandle || !existingHandle.classList.contains('image-drag-handle')) { + // 创建拖拽手柄 + const dragHandle = document.createElement('div') + dragHandle.className = 'image-drag-handle' + dragHandle.style.position = 'absolute' + dragHandle.style.top = '0.3125rem' + dragHandle.style.right = '0.3125rem' + dragHandle.style.width = '1.5rem' + dragHandle.style.height = '1.5rem' + dragHandle.style.backgroundImage = "url('/assets/icons/drawable-xxhdpi/detail_note_item_image_move.png')" + dragHandle.style.backgroundSize = 'contain' + dragHandle.style.backgroundRepeat = 'no-repeat' + dragHandle.style.cursor = 'move' + dragHandle.style.display = 'none' + dragHandle.style.zIndex = '1000' + dragHandle.style.pointerEvents = 'auto' + dragHandle.style.position = 'absolute' + + // 调试信息 + console.log('Creating drag handle for existing image') + + // 将拖拽手柄添加到图片后面 + img.insertAdjacentElement('afterend', dragHandle) + } + + // 确保拖拽手柄存在后再添加事件监听器 + setTimeout(() => { + const dragHandle = img.nextElementSibling + if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { + // 添加事件监听器来显示/隐藏拖拽手柄 + img.addEventListener('focus', function() { + dragHandle.classList.add('visible') + dragHandle.style.display = 'block' // 直接设置显示样式 + }) + + img.addEventListener('blur', function() { + // 延迟隐藏,确保用户有时间将鼠标移到手柄上 + setTimeout(() => { + if (!dragHandle.matches(':hover')) { + dragHandle.classList.remove('visible') + dragHandle.style.display = 'none' // 直接设置隐藏样式 + } + }, 100) + }) + + img.addEventListener('mouseover', function() { + dragHandle.classList.add('visible') + dragHandle.style.display = 'block' // 直接设置显示样式 + }) + + img.addEventListener('mouseout', function(e) { + // 延迟隐藏,确保用户有时间将鼠标移到手柄上 + setTimeout(() => { + if (!dragHandle.matches(':hover') && document.activeElement !== img) { + dragHandle.classList.remove('visible') + dragHandle.style.display = 'none' // 直接设置隐藏样式 + } + }, 100) + }) + + dragHandle.addEventListener('mouseover', function() { + dragHandle.classList.add('visible') + dragHandle.style.display = 'block' // 直接设置显示样式 + }) + + dragHandle.addEventListener('mouseout', function(e) { + // 延迟隐藏,确保用户有时间将鼠标移到图片上 + setTimeout(() => { + if (!img.matches(':hover') && document.activeElement !== img) { + dragHandle.classList.remove('visible') + dragHandle.style.display = 'none' // 直接设置隐藏样式 + } + }, 100) + }) + } + }, 0) + }) } }, 0) } @@ -955,7 +1217,7 @@ defineExpose({ try { editorRef.value.innerHTML = content.value console.log('Content set successfully in editorRef') - // 调整已有图片的高度 + // 调整已有图片的高度并添加拖拽功能 adjustExistingImages() } catch (error) { console.error('Failed to set innerHTML:', error) @@ -975,7 +1237,7 @@ defineExpose({ try { editorRef.value.innerHTML = content.value console.log('Content set successfully after delay') - // 调整已有图片的高度 + // 调整已有图片的高度并添加拖拽功能 adjustExistingImages() } catch (error) { console.error('Failed to set innerHTML after delay:', error) @@ -1205,6 +1467,43 @@ defineExpose({ border-radius: 0.2rem; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.18); background: var(--background-secondary); + position: relative; + outline: none; /* 移除默认焦点轮廓 */ +} + +:deep(.editor-content .editor-image.draggable) { + cursor: move; +} + +:deep(.editor-content .image-drag-handle) { + position: absolute; + top: 0.3125rem; + right: 0.3125rem; + width: 1.5rem; + height: 1.5rem; + background-image: url('/assets/icons/drawable-xxhdpi/detail_note_item_image_move.png'); + background-size: contain; + background-repeat: no-repeat; + cursor: move; + display: none; + z-index: 1000; + pointer-events: auto; /* 确保手柄可以接收鼠标事件 */ + position: absolute; +} + +:deep(.editor-content .editor-image:focus + .image-drag-handle), +:deep(.editor-content .editor-image:hover + .image-drag-handle), +:deep(.editor-content .image-drag-handle:hover) { + display: block !important; +} + +/* 强制显示拖拽手柄的类 */ +:deep(.editor-content .image-drag-handle.visible) { + display: block !important; +} + +:deep(.editor-content .editor-image.dragging) { + opacity: 0.5; } /* 待办事项样式 */