mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 05:51:26 +08:00
fix: improve note sorting behavior for drag and drop operations (#9971)
* fix: improve note sorting behavior for drag and drop operations - Skip automatic sorting when performing same-level drag reordering - Preserve treePath during same-level moves to maintain manual ordering - Return special indicator for manual reorder operations to prevent conflicts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: type safety issue --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2361c1b211
commit
d09743d254
@ -563,8 +563,10 @@ const NotesPage: FC = () => {
|
||||
const handleMoveNode = useCallback(
|
||||
async (sourceNodeId: string, targetNodeId: string, position: 'before' | 'after' | 'inside') => {
|
||||
try {
|
||||
await moveNode(sourceNodeId, targetNodeId, position)
|
||||
await sortAllLevels(sortType)
|
||||
const result = await moveNode(sourceNodeId, targetNodeId, position)
|
||||
if (result.success && result.type !== 'manual_reorder') {
|
||||
await sortAllLevels(sortType)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to move nodes:', error as Error)
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ const NOTES_TREE_ID = 'notes-tree-structure'
|
||||
|
||||
const logger = loggerService.withContext('NotesService')
|
||||
|
||||
export type MoveNodeResult = { success: false } | { success: true; type: 'file_system_move' | 'manual_reorder' }
|
||||
|
||||
/**
|
||||
* 初始化/同步笔记树结构
|
||||
*/
|
||||
@ -182,7 +184,7 @@ export async function moveNode(
|
||||
sourceNodeId: string,
|
||||
targetNodeId: string,
|
||||
position: 'before' | 'after' | 'inside'
|
||||
): Promise<boolean> {
|
||||
): Promise<MoveNodeResult> {
|
||||
try {
|
||||
const tree = await getNotesTree()
|
||||
|
||||
@ -192,19 +194,19 @@ export async function moveNode(
|
||||
|
||||
if (!sourceNode || !targetNode) {
|
||||
logger.error(`Move nodes failed: node not found (source: ${sourceNodeId}, target: ${targetNodeId})`)
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
|
||||
// 不允许文件夹被放入文件中
|
||||
if (position === 'inside' && targetNode.type === 'file' && sourceNode.type === 'folder') {
|
||||
logger.error('Move nodes failed: cannot move a folder inside a file')
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
|
||||
// 不允许将节点移动到自身内部
|
||||
if (position === 'inside' && isParentNode(tree, sourceNodeId, targetNodeId)) {
|
||||
logger.error('Move nodes failed: cannot move a node inside itself or its descendants')
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
|
||||
let targetPath: string = ''
|
||||
@ -215,7 +217,7 @@ export async function moveNode(
|
||||
targetPath = targetNode.externalPath
|
||||
} else {
|
||||
logger.error('Cannot move node inside a file node')
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
} else {
|
||||
const targetParent = findParentNode(tree, targetNodeId)
|
||||
@ -226,6 +228,20 @@ export async function moveNode(
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否为同级拖动排序
|
||||
const sourceParent = findParentNode(tree, sourceNodeId)
|
||||
const sourceDir = sourceParent ? sourceParent.externalPath : getFileDirectory(sourceNode.externalPath!)
|
||||
|
||||
const isSameLevelReorder = position !== 'inside' && sourceDir === targetPath
|
||||
|
||||
if (isSameLevelReorder) {
|
||||
// 同级拖动排序:跳过文件系统操作,只更新树结构
|
||||
logger.debug(`Same level reorder detected, skipping file system operations`)
|
||||
const success = await moveNodeInTree(tree, sourceNodeId, targetNodeId, position)
|
||||
// 返回一个特殊标识,告诉调用方这是手动排序,不需要重新自动排序
|
||||
return success ? { success: true, type: 'manual_reorder' } : { success: false }
|
||||
}
|
||||
|
||||
// 构建新的文件路径
|
||||
const sourceName = sourceNode.externalPath!.split('/').pop()!
|
||||
const sourceNameWithoutExt = sourceName.replace(sourceNode.type === 'file' ? MARKDOWN_EXT : '', '')
|
||||
@ -250,14 +266,15 @@ export async function moveNode(
|
||||
logger.debug(`Moved external ${sourceNode.type} to: ${newPath}`)
|
||||
} catch (error) {
|
||||
logger.error(`Failed to move external ${sourceNode.type}:`, error as Error)
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
}
|
||||
|
||||
return await moveNodeInTree(tree, sourceNodeId, targetNodeId, position)
|
||||
const success = await moveNodeInTree(tree, sourceNodeId, targetNodeId, position)
|
||||
return success ? { success: true, type: 'file_system_move' } : { success: false }
|
||||
} catch (error) {
|
||||
logger.error('Move nodes failed:', error as Error)
|
||||
return false
|
||||
return { success: false }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -89,8 +89,9 @@ export async function moveNodeInTree(
|
||||
return false
|
||||
}
|
||||
|
||||
// 先保存源节点的副本,以防操作失败需要恢复(暂未实现恢复逻辑)
|
||||
// const sourceNodeCopy = { ...sourceNode }
|
||||
// 在移除节点之前先获取源节点的父节点信息,用于后续判断是否为同级排序
|
||||
const sourceParent = findParentNode(tree, sourceNodeId)
|
||||
const targetParent = findParentNode(tree, targetNodeId)
|
||||
|
||||
// 从原位置移除节点(不保存数据库,只在内存中操作)
|
||||
const removed = removeNodeFromTreeInMemory(tree, sourceNodeId)
|
||||
@ -110,7 +111,6 @@ export async function moveNodeInTree(
|
||||
|
||||
sourceNode.treePath = `${targetNode.treePath}/${sourceNode.name}`
|
||||
} else {
|
||||
const targetParent = findParentNode(tree, targetNodeId)
|
||||
const targetList = targetParent ? targetParent.children! : tree
|
||||
const targetIndex = targetList.findIndex((node) => node.id === targetNodeId)
|
||||
|
||||
@ -123,11 +123,16 @@ export async function moveNodeInTree(
|
||||
const insertIndex = position === 'before' ? targetIndex : targetIndex + 1
|
||||
targetList.splice(insertIndex, 0, sourceNode)
|
||||
|
||||
// 更新节点路径
|
||||
if (targetParent) {
|
||||
sourceNode.treePath = `${targetParent.treePath}/${sourceNode.name}`
|
||||
} else {
|
||||
sourceNode.treePath = `/${sourceNode.name}`
|
||||
// 检查是否为同级排序,如果是则保持原有的 treePath
|
||||
const isSameLevelReorder = sourceParent === targetParent
|
||||
|
||||
// 只有在跨级移动时才更新节点路径
|
||||
if (!isSameLevelReorder) {
|
||||
if (targetParent) {
|
||||
sourceNode.treePath = `${targetParent.treePath}/${sourceNode.name}`
|
||||
} else {
|
||||
sourceNode.treePath = `/${sourceNode.name}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user