diff --git a/src/common/base.css b/src/common/base.css index c5ab74c..497de04 100644 --- a/src/common/base.css +++ b/src/common/base.css @@ -18,11 +18,8 @@ body { -moz-osx-font-smoothing: grayscale; } -view, -image, -text { - box-sizing: border-box; - flex-shrink: 0; +img { + user-select: none; } button { border: none; diff --git a/src/components/RichTextEditor.vue b/src/components/RichTextEditor.vue index 47c30b3..b0094e6 100644 --- a/src/components/RichTextEditor.vue +++ b/src/components/RichTextEditor.vue @@ -597,141 +597,15 @@ const insertTodoList = () => { } } -// 处理图片拖拽开始 -const handleImageDragStart = (e) => { - console.log('Drag start:', e.target) - console.log('Drag start event:', e) - e.dataTransfer.effectAllowed = 'move' - e.target.classList.add('dragging') - // Store the dragged element's index or identifier - e.dataTransfer.setData('text/plain', e.target.src) - console.log('Set drag data:', e.target.src) -} - -// 处理图片拖拽经过 -const handleImageDragOver = (e) => { - console.log('Drag over event:', e) - e.preventDefault() - e.dataTransfer.dropEffect = 'move' - - // Add visual feedback for drop target - const target = e.target - console.log('Drag over target:', target) - if (target.classList && target.classList.contains('editor-image') && !target.classList.contains('dragging')) { - // Add a visual indicator for where the image will be dropped - target.style.boxShadow = '0 0 0 2px var(--primary)' - console.log('Added visual feedback to target') - } -} - -// 处理图片拖拽释放 -const handleImageDrop = (e) => { - console.log('Drop event:', e) - e.preventDefault() - - // Get the dragged image source - const draggedImageSrc = e.dataTransfer.getData('text/plain') - console.log('Dragged image source:', draggedImageSrc) - - // Get the drop target - const target = e.target - console.log('Drop target:', target) - - // Check if we're dropping on an image - if (target.classList && target.classList.contains('editor-image')) { - console.log('Dropping on an image') - // Reset all image styles - const images = editorRef.value.querySelectorAll('.editor-image') - images.forEach(img => { - img.style.boxShadow = '0 1px 5px rgba(0, 0, 0, 0.18)' - }) - - // Find the dragged image element - const draggedImage = Array.from(images).find(img => img.src === draggedImageSrc) - console.log('Dragged image element:', draggedImage) - - if (draggedImage && draggedImage !== target) { - console.log('Valid drag operation, moving image') - // Get the parent element - const parent = target.parentNode - console.log('Parent element:', parent) - - // Create a clone of the dragged image - const clonedImage = draggedImage.cloneNode(true) - console.log('Cloned image:', clonedImage) - - // Copy over event listeners by re-adding them - clonedImage.draggable = true - clonedImage.setAttribute('data-draggable', 'true') - clonedImage.addEventListener('dragstart', handleImageDragStart) - clonedImage.addEventListener('dragover', handleImageDragOver) - clonedImage.addEventListener('drop', handleImageDrop) - clonedImage.addEventListener('dragend', handleImageDragEnd) - - // Also clone the drag handle if it exists - const originalDragHandle = draggedImage.nextElementSibling - let clonedDragHandle = null - if (originalDragHandle && originalDragHandle.classList.contains('image-drag-handle')) { - console.log('Cloning drag handle') - clonedDragHandle = originalDragHandle.cloneNode(true) - // Add event listeners to the cloned drag handle - clonedDragHandle.addEventListener('mouseover', function() { - clonedDragHandle.classList.add('visible') - clonedDragHandle.style.display = 'block' - }) - clonedDragHandle.addEventListener('mouseout', function(e) { - setTimeout(() => { - if (!clonedImage.matches(':hover') && document.activeElement !== clonedImage) { - clonedDragHandle.classList.remove('visible') - clonedDragHandle.style.display = 'none' - } - }, 100) - }) - } - - // Insert the cloned image before the target - parent.insertBefore(clonedImage, target) - console.log('Inserted cloned image') - - // Insert the drag handle if it exists - if (clonedDragHandle) { - parent.insertBefore(clonedDragHandle, clonedImage.nextSibling) - console.log('Inserted cloned drag handle') - } - - // Remove the original dragged image and its drag handle - if (originalDragHandle && originalDragHandle.classList.contains('image-drag-handle')) { - originalDragHandle.remove() - console.log('Removed original drag handle') - } - draggedImage.remove() - console.log('Removed original image') - - // Update content - handleInput() - console.log('Updated content after drag') - } else { - console.log('Invalid drag operation') - } - } else { - console.log('Not dropping on an image') - } -} - -// 处理图片拖拽结束 -const handleImageDragEnd = (evt) => { - console.log('Drag end:', evt) - - // Reset styles of all images - const images = editorRef.value.querySelectorAll('.editor-image') - images.forEach(img => { - img.style.boxShadow = '0 1px 5px rgba(0, 0, 0, 0.18)' - img.classList.remove('dragging') - }) - - // Update content after drag operation - handleInput() -} +// 图片拖拽相关状态 +const dragState = ref({ + isDragging: false, + draggedImage: null, + startY: 0, + currentY: 0, + longPressTimer: null, + isLongPress: false +}) // 插入图片 const insertImage = () => { @@ -819,106 +693,15 @@ 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) - console.log('Added drag handle to image') } tempImg.src = imageDataUrl - // 添加事件监听器来显示/隐藏拖拽手柄 - img.addEventListener('focus', function() { - console.log('Image focused') - const dragHandle = img.nextElementSibling - if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { - dragHandle.classList.add('visible') - dragHandle.style.display = 'block' // 直接设置显示样式 - } - }) - - img.addEventListener('blur', function() { - console.log('Image blurred') - 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() { - console.log('Mouse over image') - 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) { - console.log('Mouse out of image') - 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')) { - console.log('Adding event listeners to 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.setAttribute('data-draggable', 'true') - console.log('Set draggable attributes') + // 添加触摸事件监听器实现拖拽功能 + img.addEventListener('touchstart', handleTouchStart) + img.addEventListener('touchmove', handleTouchMove) + img.addEventListener('touchend', handleTouchEnd) + img.addEventListener('touchcancel', handleTouchCancel) + console.log('Added touch event listeners') // 插入图片到当前光标位置 range.insertNode(img) @@ -1009,6 +792,192 @@ const handleKeydown = e => { } } +// 处理触摸开始事件 +const handleTouchStart = (e) => { + const img = e.target + if (!img.classList.contains('editor-image')) return + + // 清除之前的定时器 + if (dragState.value.longPressTimer) { + clearTimeout(dragState.value.longPressTimer) + } + + // 设置长按检测定时器(1秒) + dragState.value.longPressTimer = setTimeout(() => { + dragState.value.isLongPress = true + dragState.value.draggedImage = img + dragState.value.startY = e.touches[0].clientY + dragState.value.currentY = e.touches[0].clientY + + // 添加拖拽样式 + img.classList.add('dragging') + img.style.opacity = '0.7' + img.style.transform = 'scale(0.95)' + img.style.zIndex = '999' + img.style.transition = 'all 0.2s ease' + + // 添加震动反馈(如果设备支持) + if (navigator.vibrate) { + navigator.vibrate(50) + } + + // 阻止页面滚动 + e.preventDefault() + }, 1000) // 1秒长按触发拖拽 +} + +// 处理触摸移动事件 +const handleTouchMove = (e) => { + if (!dragState.value.isLongPress || !dragState.value.draggedImage) return + + e.preventDefault() // 阻止页面滚动 + + const img = dragState.value.draggedImage + dragState.value.currentY = e.touches[0].clientY + + // 计算位移 + const deltaY = dragState.value.currentY - dragState.value.startY + + // 更新图片位置 + img.style.transform = `translateY(${deltaY}px) scale(0.95)` + + // 使用requestAnimationFrame优化性能 + requestAnimationFrame(() => { + // 检查与其他图片的碰撞,实现排序 + checkAndSwapImages(img, deltaY) + }) +} + +// 处理触摸结束事件 +const handleTouchEnd = (e) => { + // 清除长按定时器 + if (dragState.value.longPressTimer) { + clearTimeout(dragState.value.longPressTimer) + dragState.value.longPressTimer = null + } + + if (!dragState.value.isLongPress || !dragState.value.draggedImage) { + dragState.value.isLongPress = false + return + } + + // 重置拖拽状态 + const img = dragState.value.draggedImage + img.classList.remove('dragging') + img.style.opacity = '' + img.style.transform = '' + img.style.zIndex = '' + img.style.transition = '' + + // 添加震动反馈(如果设备支持) + if (navigator.vibrate) { + navigator.vibrate(30) + } + + // 重置状态 + dragState.value.isLongPress = false + dragState.value.draggedImage = null + dragState.value.startY = 0 + dragState.value.currentY = 0 + + // 触发内容更新 + handleInput() +} + +// 处理触摸取消事件 +const handleTouchCancel = (e) => { + // 清除长按定时器 + if (dragState.value.longPressTimer) { + clearTimeout(dragState.value.longPressTimer) + dragState.value.longPressTimer = null + } + + if (!dragState.value.isLongPress || !dragState.value.draggedImage) { + dragState.value.isLongPress = false + return + } + + // 重置拖拽状态 + const img = dragState.value.draggedImage + img.classList.remove('dragging') + img.style.opacity = '' + img.style.transform = '' + img.style.zIndex = '' + img.style.transition = '' + + // 重置状态 + dragState.value.isLongPress = false + dragState.value.draggedImage = null + dragState.value.startY = 0 + dragState.value.currentY = 0 +} + +// 检查并交换图片位置 +const checkAndSwapImages = (draggedImg, deltaY) => { + const allImages = Array.from(editorRef.value.querySelectorAll('.editor-image')) + const draggedIndex = allImages.indexOf(draggedImg) + + if (draggedIndex === -1) return + + // 计算拖拽图片的中心位置 + const draggedRect = draggedImg.getBoundingClientRect() + const draggedCenterY = draggedRect.top + draggedRect.height / 2 + deltaY + + // 查找最近的图片进行交换 + for (let i = 0; i < allImages.length; i++) { + if (i === draggedIndex) continue + + const targetImg = allImages[i] + const targetRect = targetImg.getBoundingClientRect() + const targetCenterY = targetRect.top + targetRect.height / 2 + + // 检查是否与目标图片重叠 + if (Math.abs(draggedCenterY - targetCenterY) < (draggedRect.height + targetRect.height) / 2) { + // 交换位置 + swapImages(draggedImg, targetImg) + break + } + } +} + +// 交换两张图片的位置 +const swapImages = (img1, img2) => { + const parent1 = img1.parentNode + const parent2 = img2.parentNode + + // 如果两张图片在同一父元素中 + if (parent1 === parent2) { + // 创建临时标记来帮助交换位置 + const tempMarker = document.createElement('div') + parent1.insertBefore(tempMarker, img1) + + // 交换位置 + parent1.insertBefore(img1, img2) + parent1.insertBefore(img2, tempMarker) + + // 移除临时标记 + tempMarker.remove() + + // 添加过渡效果 + img1.style.transition = 'transform 0.2s ease' + img2.style.transition = 'transform 0.2s ease' + + // 短暂延时后移除过渡效果 + setTimeout(() => { + img1.style.transition = '' + img2.style.transition = '' + }, 200) + } else { + // 不同父元素的情况(更复杂,需要特殊处理) + // 这里简化处理,实际项目中可能需要更复杂的逻辑 + const temp = document.createElement('div') + parent1.insertBefore(temp, img1) + parent2.insertBefore(img1, img2) + parent1.insertBefore(img2, temp) + temp.remove() + } +} + // 更新工具栏状态 const updateToolbarState = () => { nextTick(() => { @@ -1236,100 +1205,23 @@ const adjustExistingImages = () => { // 为现有图片添加拖拽功能 imageElements.forEach(img => { console.log('Adding drag functionality to image:', img) - // 确保图片有拖拽属性 - if (!img.hasAttribute('draggable')) { - console.log('Setting draggable attribute') - img.draggable = true - img.setAttribute('data-draggable', 'true') - - // 添加拖拽事件监听器 - img.addEventListener('dragstart', handleImageDragStart) - img.addEventListener('dragover', handleImageDragOver) - img.addEventListener('drop', handleImageDrop) - img.addEventListener('dragend', handleImageDragEnd) - console.log('Added drag event listeners') + // 添加触摸事件监听器 + if (!img.hasAttribute('data-touch-listeners')) { + console.log('Adding touch event listeners') + img.addEventListener('touchstart', handleTouchStart) + img.addEventListener('touchmove', handleTouchMove) + img.addEventListener('touchend', handleTouchEnd) + img.addEventListener('touchcancel', handleTouchCancel) + img.setAttribute('data-touch-listeners', 'true') + console.log('Added touch event listeners') } - // 检查是否已存在拖拽手柄 - let existingHandle = img.nextElementSibling - console.log('Existing handle:', existingHandle) - if (!existingHandle || !existingHandle.classList.contains('image-drag-handle')) { - console.log('Creating 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) - console.log('Added drag handle') - } - - // 确保拖拽手柄存在后再添加事件监听器 + // 移除已存在的拖拽手柄 setTimeout(() => { const dragHandle = img.nextElementSibling if (dragHandle && dragHandle.classList.contains('image-drag-handle')) { - console.log('Adding event listeners to 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) - }) + console.log('Removing existing drag handle') + dragHandle.remove() } }, 0) }) @@ -1355,18 +1247,18 @@ defineExpose({ const imageElements = editorRef.value.querySelectorAll('img.editor-image') console.log('Found images in setContent:', imageElements.length) imageElements.forEach(img => { - console.log('Adding drag listeners to image in setContent:', img) + console.log('Adding touch listeners to image in setContent:', img) // 先移除可能已有的事件监听器,避免重复 - img.removeEventListener('dragstart', handleImageDragStart) - img.removeEventListener('dragover', handleImageDragOver) - img.removeEventListener('drop', handleImageDrop) - img.removeEventListener('dragend', handleImageDragEnd) + img.removeEventListener('touchstart', handleTouchStart) + img.removeEventListener('touchmove', handleTouchMove) + img.removeEventListener('touchend', handleTouchEnd) + img.removeEventListener('touchcancel', handleTouchCancel) // 重新添加事件监听器 - img.addEventListener('dragstart', handleImageDragStart) - img.addEventListener('dragover', handleImageDragOver) - img.addEventListener('drop', handleImageDrop) - img.addEventListener('dragend', handleImageDragEnd) + img.addEventListener('touchstart', handleTouchStart) + img.addEventListener('touchmove', handleTouchMove) + img.addEventListener('touchend', handleTouchEnd) + img.addEventListener('touchcancel', handleTouchCancel) }) }, 0) } catch (error) { @@ -1395,18 +1287,18 @@ defineExpose({ const imageElements = editorRef.value.querySelectorAll('img.editor-image') console.log('Found images in delayed setContent:', imageElements.length) imageElements.forEach(img => { - console.log('Adding drag listeners to image in delayed setContent:', img) + console.log('Adding touch listeners to image in delayed setContent:', img) // 先移除可能已有的事件监听器,避免重复 - img.removeEventListener('dragstart', handleImageDragStart) - img.removeEventListener('dragover', handleImageDragOver) - img.removeEventListener('drop', handleImageDrop) - img.removeEventListener('dragend', handleImageDragEnd) + img.removeEventListener('touchstart', handleTouchStart) + img.removeEventListener('touchmove', handleTouchMove) + img.removeEventListener('touchend', handleTouchEnd) + img.removeEventListener('touchcancel', handleTouchCancel) // 重新添加事件监听器 - img.addEventListener('dragstart', handleImageDragStart) - img.addEventListener('dragover', handleImageDragOver) - img.addEventListener('drop', handleImageDrop) - img.addEventListener('dragend', handleImageDragEnd) + img.addEventListener('touchstart', handleTouchStart) + img.addEventListener('touchmove', handleTouchMove) + img.addEventListener('touchend', handleTouchEnd) + img.addEventListener('touchcancel', handleTouchCancel) }) }, 0) } catch (error) { @@ -1645,35 +1537,12 @@ defineExpose({ 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; + opacity: 0.7; + transform: scale(0.95); + z-index: 999; + transition: all 0.2s ease; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); } /* 待办事项样式 */