You've already forked SmartisanNote.Remake
Merge pull request 'future' (#14) from future into main
Reviewed-on: yuantao/SmartisanNote.Remake#14
This commit is contained in:
@@ -48,7 +48,7 @@ const dragState = ref({
|
|||||||
// 统一的事件监听器管理器
|
// 统一的事件监听器管理器
|
||||||
const eventManager = {
|
const eventManager = {
|
||||||
// 为图片容器添加事件监听器
|
// 为图片容器添加事件监听器
|
||||||
addImageContainerListeners(container, deleteBtn) {
|
addImageContainerListeners(container, deleteBtn, downloadBtn, previewBtn, cropBtn) {
|
||||||
// 先移除可能已有的事件监听器,避免重复
|
// 先移除可能已有的事件监听器,避免重复
|
||||||
this.removeImageContainerListeners(container)
|
this.removeImageContainerListeners(container)
|
||||||
|
|
||||||
@@ -83,24 +83,219 @@ const eventManager = {
|
|||||||
deleteBtn._deleteHandler = deleteHandler
|
deleteBtn._deleteHandler = deleteHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为图片容器添加短按事件以显示/隐藏删除按钮
|
// 为下载按钮添加点击事件
|
||||||
let touchStartTime = 0
|
if (downloadBtn) {
|
||||||
let isDeleteButtonClicked = false
|
const downloadHandler = function (e) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
// 标记删除按钮被点击
|
// 检查下载按钮是否可见,只有在可见状态下才能触发下载
|
||||||
const markDeleteButtonClicked = function () {
|
if (downloadBtn.classList.contains('visible')) {
|
||||||
isDeleteButtonClicked = true
|
// 检查是否是刚显示的按钮点击(通过时间戳判断)
|
||||||
|
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(() => {
|
setTimeout(() => {
|
||||||
isDeleteButtonClicked = false
|
isAnyButtonClicked = false
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果删除按钮存在,为其添加标记事件
|
// 为所有按钮添加标记事件
|
||||||
if (deleteBtn) {
|
const allButtons = [deleteBtn, downloadBtn, previewBtn, cropBtn].filter(btn => btn)
|
||||||
deleteBtn._markClickHandler = markDeleteButtonClicked
|
allButtons.forEach(btn => {
|
||||||
deleteBtn.addEventListener('touchstart', markDeleteButtonClicked)
|
btn._markClickHandler = markButtonClicked
|
||||||
}
|
btn.addEventListener('touchstart', markButtonClicked)
|
||||||
|
})
|
||||||
|
|
||||||
const touchStartHandler = function (e) {
|
const touchStartHandler = function (e) {
|
||||||
touchStartTime = Date.now()
|
touchStartTime = Date.now()
|
||||||
@@ -108,24 +303,27 @@ const eventManager = {
|
|||||||
|
|
||||||
const touchEndHandler = function (e) {
|
const touchEndHandler = function (e) {
|
||||||
const touchDuration = Date.now() - touchStartTime
|
const touchDuration = Date.now() - touchStartTime
|
||||||
// 短按(小于200ms)且非长按拖拽状态且不是删除按钮点击时切换删除按钮显示
|
// 短按(小于200ms)且非长按拖拽状态且不是按钮点击时切换所有按钮显示
|
||||||
if (touchDuration < 200 && !dragState.value.isLongPress && !isDeleteButtonClicked) {
|
if (touchDuration < 200 && !dragState.value.isLongPress && !isAnyButtonClicked) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
// 切换删除按钮的显示状态
|
// 切换所有按钮的显示状态
|
||||||
if (deleteBtn) {
|
const allButtons = [deleteBtn, downloadBtn, previewBtn, cropBtn].filter(btn => btn)
|
||||||
const isCurrentlyVisible = deleteBtn.classList.contains('visible')
|
if (allButtons.length > 0) {
|
||||||
|
const isCurrentlyVisible = allButtons[0].classList.contains('visible')
|
||||||
|
allButtons.forEach(btn => {
|
||||||
if (isCurrentlyVisible) {
|
if (isCurrentlyVisible) {
|
||||||
deleteBtn.classList.remove('visible')
|
btn.classList.remove('visible')
|
||||||
} else {
|
} else {
|
||||||
deleteBtn.classList.add('visible')
|
btn.classList.add('visible')
|
||||||
// 记录显示时间
|
// 记录显示时间
|
||||||
deleteBtn._lastVisibleTime = Date.now()
|
btn._lastVisibleTime = Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// 重置按钮点击标记
|
||||||
// 重置删除按钮点击标记
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
isDeleteButtonClicked = false
|
isAnyButtonClicked = false
|
||||||
}, 50)
|
}, 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +333,7 @@ const eventManager = {
|
|||||||
// 保存事件处理函数的引用,以便后续移除
|
// 保存事件处理函数的引用,以便后续移除
|
||||||
container._touchStartHandler = touchStartHandler
|
container._touchStartHandler = touchStartHandler
|
||||||
container._touchEndHandler = touchEndHandler
|
container._touchEndHandler = touchEndHandler
|
||||||
container._markDeleteButtonClicked = markDeleteButtonClicked
|
container._markButtonClicked = markButtonClicked
|
||||||
},
|
},
|
||||||
|
|
||||||
// 移除图片容器的事件监听器
|
// 移除图片容器的事件监听器
|
||||||
@@ -149,7 +347,7 @@ const eventManager = {
|
|||||||
// 移除短按事件监听器
|
// 移除短按事件监听器
|
||||||
const touchStartHandler = container._touchStartHandler
|
const touchStartHandler = container._touchStartHandler
|
||||||
const touchEndHandler = container._touchEndHandler
|
const touchEndHandler = container._touchEndHandler
|
||||||
const markDeleteButtonClicked = container._markDeleteButtonClicked
|
const markButtonClicked = container._markButtonClicked
|
||||||
|
|
||||||
if (touchStartHandler) {
|
if (touchStartHandler) {
|
||||||
container.removeEventListener('touchstart', touchStartHandler)
|
container.removeEventListener('touchstart', touchStartHandler)
|
||||||
@@ -161,23 +359,34 @@ const eventManager = {
|
|||||||
delete container._touchEndHandler
|
delete container._touchEndHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markDeleteButtonClicked) {
|
if (markButtonClicked) {
|
||||||
delete container._markDeleteButtonClicked
|
delete container._markButtonClicked
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除删除按钮事件监听器
|
// 移除所有按钮事件监听器
|
||||||
const deleteBtn = container.querySelector('.image-delete-btn')
|
const deleteBtn = container.querySelector('.image-delete-btn')
|
||||||
if (deleteBtn) {
|
const downloadBtn = container.querySelector('.image-download-btn')
|
||||||
if (deleteBtn._deleteHandler) {
|
const previewBtn = container.querySelector('.image-preview-btn')
|
||||||
deleteBtn.removeEventListener('click', deleteBtn._deleteHandler)
|
const cropBtn = container.querySelector('.image-crop-btn')
|
||||||
delete deleteBtn._deleteHandler
|
|
||||||
|
const removeButtonListeners = (btn, handlerName, markHandlerName) => {
|
||||||
|
if (btn) {
|
||||||
|
if (btn[handlerName]) {
|
||||||
|
btn.removeEventListener('click', btn[handlerName])
|
||||||
|
delete btn[handlerName]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteBtn._markClickHandler) {
|
if (btn[markHandlerName]) {
|
||||||
deleteBtn.removeEventListener('touchstart', deleteBtn._markClickHandler)
|
btn.removeEventListener('touchstart', btn[markHandlerName])
|
||||||
delete deleteBtn._markClickHandler
|
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)
|
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)
|
}, 0)
|
||||||
})
|
})
|
||||||
@@ -714,14 +950,29 @@ const createImageContainer = imageDataUrl => {
|
|||||||
const deleteBtn = document.createElement('div')
|
const deleteBtn = document.createElement('div')
|
||||||
deleteBtn.className = 'image-delete-btn'
|
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(img)
|
||||||
imgContainer.appendChild(deleteBtn)
|
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)
|
adjustImageSize(img)
|
||||||
@@ -1424,11 +1675,38 @@ const wrapOrphanedImages = () => {
|
|||||||
// 如果删除按钮不存在,创建它
|
// 如果删除按钮不存在,创建它
|
||||||
deleteBtn = document.createElement('div')
|
deleteBtn = document.createElement('div')
|
||||||
deleteBtn.className = 'image-delete-btn'
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1477,8 +1755,35 @@ const adjustExistingImages = () => {
|
|||||||
container.appendChild(deleteBtn)
|
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')
|
img.setAttribute('data-touch-listeners', 'true')
|
||||||
})
|
})
|
||||||
@@ -1518,6 +1823,24 @@ const cleanContentForSave = () => {
|
|||||||
btn.classList.remove('visible')
|
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')
|
const indicators = tempDiv.querySelectorAll('.drag-indicator')
|
||||||
indicators.forEach(indicator => {
|
indicators.forEach(indicator => {
|
||||||
@@ -1818,7 +2141,67 @@ defineExpose({
|
|||||||
opacity: 0;
|
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;
|
pointer-events: auto;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@
|
|||||||
<Header v-else ref="headerRef" :onBack="handleCancel" :onAction="handleAction" actionIcon="preview" />
|
<Header v-else ref="headerRef" :onBack="handleCancel" :onAction="handleAction" actionIcon="preview" />
|
||||||
<section>
|
<section>
|
||||||
<!-- 顶部信息栏 -->
|
<!-- 顶部信息栏 -->
|
||||||
<div class="header-info">
|
<div ref="headerInfoRef" class="header-info">
|
||||||
<span class="edit-time">{{ formattedTime }}</span>
|
<span class="edit-time">{{ formattedTime }}</span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span class="word-count">{{ wordCount }}</span>
|
<span class="word-count">{{ wordCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 富文本编辑器 -->
|
<!-- 富文本编辑器 -->
|
||||||
<div class="editor-container">
|
<div class="note-container" :style="{ height: noteContainerHeight }">
|
||||||
<RichTextEditor ref="editorRef" :modelValue="content" @update:modelValue="handleContentChange" @focus="handleEditorFocus" @blur="handleEditorBlur" class="rich-text-editor" />
|
<RichTextEditor ref="editorRef" :modelValue="content" @update:modelValue="handleContentChange" @focus="handleEditorFocus" @blur="handleEditorBlur" class="rich-text-editor" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -46,9 +46,13 @@ const store = useAppStore()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const editorRef = ref(null)
|
const editorRef = ref(null)
|
||||||
const headerRef = ref(null)
|
const headerRef = ref(null)
|
||||||
|
const headerInfoRef = ref(null)
|
||||||
// 是否聚焦编辑器
|
// 是否聚焦编辑器
|
||||||
const isEditorFocus = ref(false)
|
const isEditorFocus = ref(false)
|
||||||
|
|
||||||
|
// 计算.note-container的高度
|
||||||
|
const noteContainerHeight = ref('100vh')
|
||||||
|
|
||||||
// 设置便签内容的函数
|
// 设置便签内容的函数
|
||||||
// 用于在编辑器中加载指定便签的内容
|
// 用于在编辑器中加载指定便签的内容
|
||||||
const setNoteContent = async noteId => {
|
const setNoteContent = async noteId => {
|
||||||
@@ -81,6 +85,9 @@ onMounted(async () => {
|
|||||||
if (noteId.value) {
|
if (noteId.value) {
|
||||||
await setNoteContent(noteId.value)
|
await setNoteContent(noteId.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 等待DOM更新后计算.note-container的高度
|
||||||
|
calculateNoteContainerHeight()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听noteId变化,确保在编辑器准备好后设置内容
|
// 监听noteId变化,确保在编辑器准备好后设置内容
|
||||||
@@ -89,6 +96,8 @@ watch(
|
|||||||
async newNoteId => {
|
async newNoteId => {
|
||||||
if (newNoteId) {
|
if (newNoteId) {
|
||||||
await setNoteContent(newNoteId)
|
await setNoteContent(newNoteId)
|
||||||
|
// 重新计算.note-container的高度
|
||||||
|
calculateNoteContainerHeight()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
@@ -100,6 +109,8 @@ watch(
|
|||||||
async newNotes => {
|
async newNotes => {
|
||||||
if (noteId.value && newNotes.length > 0) {
|
if (noteId.value && newNotes.length > 0) {
|
||||||
await setNoteContent(noteId.value)
|
await setNoteContent(noteId.value)
|
||||||
|
// 重新计算.note-container的高度
|
||||||
|
calculateNoteContainerHeight()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
@@ -127,6 +138,8 @@ watch(
|
|||||||
async newNotes => {
|
async newNotes => {
|
||||||
if (noteId.value && newNotes.length > 0) {
|
if (noteId.value && newNotes.length > 0) {
|
||||||
await setNoteContent(noteId.value)
|
await setNoteContent(noteId.value)
|
||||||
|
// 重新计算.note-container的高度
|
||||||
|
calculateNoteContainerHeight()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
@@ -329,11 +342,19 @@ const setShowAlert = value => {
|
|||||||
// 处理编辑器获得焦点
|
// 处理编辑器获得焦点
|
||||||
const handleEditorFocus = () => {
|
const handleEditorFocus = () => {
|
||||||
isEditorFocus.value = true
|
isEditorFocus.value = true
|
||||||
|
// 重新计算.note-container的高度
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理编辑器失去焦点
|
// 处理编辑器失去焦点
|
||||||
const handleEditorBlur = () => {
|
const handleEditorBlur = () => {
|
||||||
isEditorFocus.value = false
|
isEditorFocus.value = false
|
||||||
|
// 重新计算.note-container的高度
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理删除便签
|
// 处理删除便签
|
||||||
@@ -395,9 +416,70 @@ const handleShare = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算.note-container的高度
|
||||||
|
const calculateNoteContainerHeight = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
let headerHeight = 0
|
||||||
|
let headerInfoHeight = 0
|
||||||
|
|
||||||
|
// 获取Header组件的高度
|
||||||
|
if (headerRef.value?.$el) {
|
||||||
|
headerHeight = headerRef.value.$el.offsetHeight || 0
|
||||||
|
} else {
|
||||||
|
// 如果headerRef不可用,尝试查找Header组件的DOM元素
|
||||||
|
const headerElement = document.querySelector('.component')
|
||||||
|
headerHeight = headerElement ? headerElement.offsetHeight : 100 // 默认高度
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取.header-info的高度
|
||||||
|
if (headerInfoRef.value) {
|
||||||
|
headerInfoHeight = headerInfoRef.value.offsetHeight || 0
|
||||||
|
} else {
|
||||||
|
// 如果headerInfoRef不可用,获取.header-info的默认高度
|
||||||
|
const headerInfoElement = document.querySelector('.header-info')
|
||||||
|
headerInfoHeight = headerInfoElement ? headerInfoElement.offsetHeight : 40 // 默认高度
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算剩余高度 (屏幕高度 - Header高度 - HeaderInfo高度)
|
||||||
|
const totalHeaderHeight = headerHeight + headerInfoHeight
|
||||||
|
const screenHeight = window.innerHeight
|
||||||
|
const newHeight = screenHeight - totalHeaderHeight
|
||||||
|
|
||||||
|
// 设置.note-container的高度
|
||||||
|
noteContainerHeight.value = `${newHeight}px`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在组件挂载后和内容变化时重新计算高度
|
||||||
|
onMounted(() => {
|
||||||
|
// 等待DOM更新后再计算高度
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听窗口大小变化事件
|
||||||
|
window.addEventListener('resize', calculateNoteContainerHeight)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听编辑器焦点变化,重新计算高度
|
||||||
|
watch(isEditorFocus, () => {
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听内容变化,重新计算高度(当内容变化可能导致header-info高度变化时)
|
||||||
|
watch(content, () => {
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// 在组件卸载前自动保存或删除
|
// 在组件卸载前自动保存或删除
|
||||||
onBeforeUnmount(async () => {
|
onBeforeUnmount(async () => {
|
||||||
await autoSaveNote()
|
await autoSaveNote()
|
||||||
|
// 移除窗口大小变化事件监听器
|
||||||
|
window.removeEventListener('resize', calculateNoteContainerHeight)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -413,14 +495,10 @@ onBeforeUnmount(async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-container {
|
.note-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 100%;
|
overflow-y: scroll;
|
||||||
overflow-y: auto;
|
background-color: var(--background);
|
||||||
background-color: var(--background-card);
|
|
||||||
&.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rich-text-editor {
|
.rich-text-editor {
|
||||||
|
|||||||
@@ -101,23 +101,7 @@ export const useAppStore = defineStore('app', {
|
|||||||
|
|
||||||
// Mock folders - 使用固定的日期值
|
// Mock folders - 使用固定的日期值
|
||||||
// 预设的文件夹示例数据
|
// 预设的文件夹示例数据
|
||||||
const mockFolders = [
|
const mockFolders = []
|
||||||
{
|
|
||||||
id: 'folder1',
|
|
||||||
name: '工作',
|
|
||||||
createdAt: '2025-10-12T10:00:00.000Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'folder2',
|
|
||||||
name: '个人',
|
|
||||||
createdAt: '2025-10-12T10:00:00.000Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'folder3',
|
|
||||||
name: '学习',
|
|
||||||
createdAt: '2025-10-12T10:00:00.000Z',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// Mock settings
|
// Mock settings
|
||||||
// 预设的设置示例数据
|
// 预设的设置示例数据
|
||||||
|
|||||||
Reference in New Issue
Block a user