@@ -140,7 +140,7 @@ export default function ChatHistoryTree({ onChatClick }: ChatHistoryTreeProps) {
140140 if ( ! dragNodeInfo || ! dropNodeInfo ) return
141141
142142 const { dropPosition, dropToGap } = info
143-
143+
144144 // 如果拖拽到文件夹内部(非gap位置)
145145 if ( ! dropToGap && dropNodeInfo . type === 'folder' ) {
146146 // 检查是否会形成循环引用(文件夹不能拖入自己的子文件夹中)
@@ -158,24 +158,39 @@ export default function ChatHistoryTree({ onChatClick }: ChatHistoryTreeProps) {
158158 console . warn ( 'Cannot move folder: would create a cycle' )
159159 return
160160 }
161+ }
161162
162- // 获取目标文件夹下的最大order值(包括文件夹和聊天)
163- const folderChildren = folders . filter ( ( f ) => f . parentId === dropNodeInfo . id )
164- const folderChats = pages . filter ( ( chat ) => chat . folderId === dropNodeInfo . id )
165- const allOrders = [
166- ...folderChildren . map ( ( f ) => f . order || 0 ) ,
167- ...folderChats . map ( ( c ) => c . order || 0 )
168- ]
169- const maxOrder = allOrders . length > 0 ? Math . max ( ...allOrders ) : 0
163+ // 获取目标文件夹
164+ const targetFolder = folders . find ( f => f . id === dropNodeInfo . id )
165+
166+ // 如果文件夹是展开的,且有子节点,根据dropPosition确定插入位置
167+ // dropPosition为0表示放在文件夹内部作为第一个子节点
168+ // 否则作为最后一个子节点
169+ const folderChildren = folders . filter ( ( f ) => f . parentId === dropNodeInfo . id )
170+ . map ( f => ( { type : 'folder' as const , id : f . id , order : f . order || 0 } ) )
171+ const folderChats = pages . filter ( ( chat ) => chat . folderId === dropNodeInfo . id && chat . type !== 'settings' )
172+ . map ( c => ( { type : 'chat' as const , id : c . id , order : c . order || 0 } ) )
173+ const allChildren = [ ...folderChildren , ...folderChats ] . sort ( ( a , b ) => a . order - b . order )
174+
175+ let newOrder : number
176+
177+ // 如果文件夹是展开的,且dropPosition为0,放在最前面
178+ // 注意:Ant Design Tree的dropPosition在拖入文件夹时通常为0
179+ if ( targetFolder ?. expanded && allChildren . length > 0 ) {
180+ // 总是放在最前面,因为拖入文件夹通常意味着作为第一个子节点
181+ newOrder = allChildren [ 0 ] . order - 1000
182+ } else if ( allChildren . length > 0 ) {
183+ // 如果文件夹未展开或dropPosition不为0,放在最后
184+ newOrder = allChildren [ allChildren . length - 1 ] . order + 1000
185+ } else {
186+ // 文件夹为空,使用默认值
187+ newOrder = 1000
188+ }
170189
171- moveFolder ( dragNodeInfo . id , maxOrder + 1000 , dropNodeInfo . id )
190+ if ( dragNodeInfo . type === 'folder' ) {
191+ moveFolder ( dragNodeInfo . id , newOrder , dropNodeInfo . id )
172192 } else if ( dragNodeInfo . type === 'chat' ) {
173- // 将聊天移动到文件夹内,获取该文件夹下聊天的最大order值
174- const folderChats = pages . filter ( ( chat ) => chat . folderId === dropNodeInfo . id )
175- const maxOrder =
176- folderChats . length > 0 ? Math . max ( ...folderChats . map ( ( chat ) => chat . order || 0 ) ) : 0
177-
178- movePage ( dragNodeInfo . id , dropNodeInfo . id , maxOrder + 1000 )
193+ movePage ( dragNodeInfo . id , dropNodeInfo . id , newOrder )
179194 }
180195 return
181196 }
@@ -221,55 +236,68 @@ export default function ChatHistoryTree({ onChatClick }: ChatHistoryTreeProps) {
221236 }
222237 }
223238
239+ // 获取目标位置的所有同级节点(包括文件夹和聊天)
240+ const allSiblings = [
241+ ...folders
242+ . filter ( ( folder ) => folder . parentId === targetParentId )
243+ . map ( ( folder ) => ( { type : 'folder' as const , id : folder . id , order : folder . order || 0 } ) ) ,
244+ ...pages
245+ . filter ( ( chat ) => chat . folderId === targetParentId && chat . type !== 'settings' )
246+ . map ( ( chat ) => ( { type : 'chat' as const , id : chat . id , order : chat . order || 0 } ) )
247+ ]
248+ . filter ( ( item ) => ! ( item . type === dragNodeInfo . type && item . id === dragNodeInfo . id ) ) // 排除拖拽节点自身
249+ . sort ( ( a , b ) => a . order - b . order )
250+
251+ // 找到目标节点在同级节点中的位置
252+ const dropIndex = allSiblings . findIndex (
253+ ( item ) => item . type === dropNodeInfo . type && item . id === dropNodeInfo . id
254+ )
255+
224256 // 计算新的order值
225257 let newOrder : number = Date . now ( )
226258
227- // 根据节点类型计算order
228- if ( dragNodeInfo . type === 'chat' ) {
229- const siblings = pages
230- . filter ( ( chat ) => chat . folderId === targetParentId && chat . id !== dragNodeInfo . id )
231- . sort ( ( a , b ) => ( a . order || 0 ) - ( b . order || 0 ) )
232-
233- const dropIndex = siblings . findIndex ( ( item ) => item . id === dropNodeInfo . id )
234- if ( dropIndex >= 0 ) {
235- if ( dropPosition <= dropIndex ) {
236- // 拖拽到前面
237- newOrder =
238- dropIndex === 0
239- ? ( siblings [ 0 ] ?. order || 0 ) - 1000
240- : ( ( siblings [ dropIndex - 1 ] ?. order || 0 ) + ( siblings [ dropIndex ] ?. order || 0 ) ) / 2
259+ if ( dropIndex >= 0 ) {
260+ // Ant Design Tree的dropPosition含义:
261+ // - 相对于目标节点的位置索引
262+ // - dropPosition === -1: 拖到目标节点上方
263+ // - dropPosition === 1: 拖到目标节点下方
264+ // - dropPosition > 1: 拖到更后面的位置
265+
266+ // 判断是插入到目标节点的前面还是后面
267+ const insertBefore = dropPosition === - 1 || ( dropPosition > 0 && dropPosition <= dropIndex )
268+
269+ if ( insertBefore ) {
270+ // 插入到目标节点前面
271+ if ( dropIndex === 0 ) {
272+ // 插入到第一个位置
273+ newOrder = allSiblings [ 0 ] . order - 1000
241274 } else {
242- // 拖拽到后面
243- newOrder =
244- dropIndex === siblings . length - 1
245- ? ( siblings [ dropIndex ] ?. order || 0 ) + 1000
246- : ( ( siblings [ dropIndex ] ?. order || 0 ) + ( siblings [ dropIndex + 1 ] ?. order || 0 ) ) / 2
275+ // 插入到中间,取前一个节点和目标节点order的平均值
276+ const prevOrder = allSiblings [ dropIndex - 1 ] . order
277+ const currentOrder = allSiblings [ dropIndex ] . order
278+ newOrder = ( prevOrder + currentOrder ) / 2
247279 }
248- }
249-
250- movePage ( dragNodeInfo . id , targetParentId , newOrder )
251- } else if ( dragNodeInfo . type === 'folder' ) {
252- const siblings = folders
253- . filter ( ( folder ) => folder . parentId === targetParentId && folder . id !== dragNodeInfo . id )
254- . sort ( ( a , b ) => ( a . order || 0 ) - ( b . order || 0 ) )
255-
256- const dropIndex = siblings . findIndex ( ( item ) => item . id === dropNodeInfo . id )
257- if ( dropIndex >= 0 ) {
258- if ( dropPosition <= dropIndex ) {
259- // 拖拽到前面
260- newOrder =
261- dropIndex === 0
262- ? ( siblings [ 0 ] ?. order || 0 ) - 1000
263- : ( ( siblings [ dropIndex - 1 ] ?. order || 0 ) + ( siblings [ dropIndex ] ?. order || 0 ) ) / 2
280+ } else {
281+ // 插入到目标节点后面
282+ if ( dropIndex === allSiblings . length - 1 ) {
283+ // 插入到最后一个位置
284+ newOrder = allSiblings [ dropIndex ] . order + 1000
264285 } else {
265- // 拖拽到后面
266- newOrder =
267- dropIndex === siblings . length - 1
268- ? ( siblings [ dropIndex ] ?. order || 0 ) + 1000
269- : ( ( siblings [ dropIndex ] ?. order || 0 ) + ( siblings [ dropIndex + 1 ] ?. order || 0 ) ) / 2
286+ // 插入到中间,取目标节点和下一个节点order的平均值
287+ const currentOrder = allSiblings [ dropIndex ] . order
288+ const nextOrder = allSiblings [ dropIndex + 1 ] . order
289+ newOrder = ( currentOrder + nextOrder ) / 2
270290 }
271291 }
292+ } else {
293+ // 如果找不到目标节点,添加到末尾
294+ newOrder = allSiblings . length > 0 ? allSiblings [ allSiblings . length - 1 ] . order + 1000 : 1000
295+ }
272296
297+ // 根据拖拽节点类型移动节点
298+ if ( dragNodeInfo . type === 'chat' ) {
299+ movePage ( dragNodeInfo . id , targetParentId , newOrder )
300+ } else if ( dragNodeInfo . type === 'folder' ) {
273301 moveFolder ( dragNodeInfo . id , newOrder , targetParentId )
274302 }
275303 }
@@ -280,55 +308,65 @@ export default function ChatHistoryTree({ onChatClick }: ChatHistoryTreeProps) {
280308 // 递归构建文件夹树
281309 const buildFolderTree = useCallback (
282310 ( parentId ?: string ) : DataNode [ ] => {
283- const result : DataNode [ ] = [ ]
284-
285311 // 获取指定父级下的文件夹
286312 const childFolders = folders
287313 . filter ( ( folder ) => folder . parentId === parentId )
288- . sort ( ( a , b ) => ( a . order || 0 ) - ( b . order || 0 ) )
314+ . map ( folder => ( {
315+ type : 'folder' as const ,
316+ data : folder ,
317+ order : folder . order || 0
318+ } ) )
289319
290320 // 获取指定父级下的聊天(过滤掉设置页面)
291321 const chats = pages
292322 . filter ( ( chat ) => chat . folderId === parentId && chat . type !== 'settings' )
293- . sort ( ( a , b ) => ( a . order || 0 ) - ( b . order || 0 ) )
294-
295- // 添加文件夹
296- childFolders . forEach ( ( folder ) => {
297- const folderChildren = buildFolderTree ( folder . id )
298-
299- result . push ( {
300- key : `folder-${ folder . id } ` ,
301- title : (
302- < ChatHistoryTreeNode
303- type = "folder"
304- data = { folder }
305- onEdit = { ( ) => handleNodeEdit ( folder . id , 'folder' ) }
306- onDelete = { ( ) => handleDeleteFolder ( folder . id ) }
307- onCreate = { ( type ) => handleNodeCreate ( folder . id , type ) }
308- onSaveEdit = { handleSaveEdit }
309- />
310- ) ,
311- checkable : false ,
312- children : folderChildren
313- } )
314- } )
315-
316- // 添加聊天
317- chats . forEach ( ( chat ) => {
318- result . push ( {
319- key : `chat-${ chat . id } ` ,
320- title : (
321- < ChatHistoryTreeNode
322- type = "chat"
323- data = { chat }
324- onEdit = { ( ) => handleNodeEdit ( chat . id , 'chat' ) }
325- onDelete = { ( ) => handleDeleteChat ( chat . id ) }
326- onChatClick = { onChatClick }
327- onSaveEdit = { handleSaveEdit }
328- />
329- ) ,
330- isLeaf : true
331- } )
323+ . map ( chat => ( {
324+ type : 'chat' as const ,
325+ data : chat ,
326+ order : chat . order || 0
327+ } ) )
328+
329+ // 合并文件夹和聊天,按order排序
330+ const allItems = [ ...childFolders , ...chats ] . sort ( ( a , b ) => a . order - b . order )
331+
332+ // 构建树节点
333+ const result : DataNode [ ] = allItems . map ( item => {
334+ if ( item . type === 'folder' ) {
335+ const folder = item . data as typeof folders [ 0 ]
336+ const folderChildren = buildFolderTree ( folder . id )
337+
338+ return {
339+ key : `folder-${ folder . id } ` ,
340+ title : (
341+ < ChatHistoryTreeNode
342+ type = "folder"
343+ data = { folder }
344+ onEdit = { ( ) => handleNodeEdit ( folder . id , 'folder' ) }
345+ onDelete = { ( ) => handleDeleteFolder ( folder . id ) }
346+ onCreate = { ( type ) => handleNodeCreate ( folder . id , type ) }
347+ onSaveEdit = { handleSaveEdit }
348+ />
349+ ) ,
350+ checkable : false ,
351+ children : folderChildren
352+ }
353+ } else {
354+ const chat = item . data as typeof pages [ 0 ]
355+ return {
356+ key : `chat-${ chat . id } ` ,
357+ title : (
358+ < ChatHistoryTreeNode
359+ type = "chat"
360+ data = { chat }
361+ onEdit = { ( ) => handleNodeEdit ( chat . id , 'chat' ) }
362+ onDelete = { ( ) => handleDeleteChat ( chat . id ) }
363+ onChatClick = { onChatClick }
364+ onSaveEdit = { handleSaveEdit }
365+ />
366+ ) ,
367+ isLeaf : true
368+ }
369+ }
332370 } )
333371
334372 return result
0 commit comments