/** * 收藏夹管理 Composable * 提供收藏文件的添加、删除、排序等功能 */ import { ref } from 'vue' import { STORAGE_KEYS, DEFAULTS } from '@/utils/constants' import { getPathSeparator } from '@/utils/fileUtils' import { Message } from '@arco-design/web-vue' import type { FavoriteFile, FileItem, DraggingState } from '@/types/file-system' export function useFavorites() { // 收藏列表 const favorites = ref([]) // 拖拽状态 const draggingState = ref({ isDragging: false, draggedIndex: -1, pressedIndex: -1 }) /** * 排序收藏列表:置顶项在前(按 pinnedAt 降序),非置顶项按添加时间降序 */ const sortFavorites = () => { favorites.value = [...favorites.value].sort((a, b) => { // 置顶项优先 if (a.pinnedAt && !b.pinnedAt) return -1 if (!a.pinnedAt && b.pinnedAt) return 1 // 都是置顶项,按置顶时间降序 if (a.pinnedAt && b.pinnedAt) return b.pinnedAt - a.pinnedAt // 都不是置顶项,按添加时间降序(最新在前) return b.addedAt - a.addedAt }) } /** * 从 localStorage 加载收藏列表 */ const loadFavorites = () => { try { const stored = localStorage.getItem(STORAGE_KEYS.FAVORITE_FILES) if (stored) { const loaded = JSON.parse(stored) as FavoriteFile[] // 数据迁移:将旧字段 is_dir 转换为 isDir favorites.value = loaded.map(fav => ({ ...fav, isDir: fav.isDir ?? (fav as any).is_dir ?? false })) // 排序 sortFavorites() } } catch (error) { console.error('加载收藏列表失败:', error) } } /** * 保存收藏列表到 localStorage */ const saveFavorites = () => { try { localStorage.setItem(STORAGE_KEYS.FAVORITE_FILES, JSON.stringify(favorites.value)) } catch (error) { console.error('保存收藏列表失败:', error) } } /** * 标准化路径用于比较(Windows 大小写不敏感) */ const normalizePath = (path: string): string => { return path.toLowerCase() } /** * 添加收藏 */ const addFavorite = (file: FileItem) => { if (isFavorite(file.path)) { return false } if (favorites.value.length >= DEFAULTS.MAX_FAVORITES_LENGTH) { Message.warning(`收藏夹已满,最多收藏 ${DEFAULTS.MAX_FAVORITES_LENGTH} 项`) return false } favorites.value.push({ ...file, addedAt: Date.now() } as FavoriteFile) sortFavorites() saveFavorites() return true } /** * 删除收藏 */ const removeFavorite = (path: string) => { const normalizedPath = normalizePath(path) const index = favorites.value.findIndex(fav => normalizePath(fav.path) === normalizedPath) if (index !== -1) { favorites.value.splice(index, 1) saveFavorites() } } /** * 切换收藏状态 */ const toggleFavorite = (file: FileItem) => { if (isFavorite(file.path)) { removeFavorite(file.path) return false } addFavorite(file) return true } /** * 检查是否已收藏 */ const isFavorite = (path: string): boolean => { const normalizedPath = normalizePath(path) return favorites.value.some(fav => normalizePath(fav.path) === normalizedPath) } /** * 切换置顶状态 */ const togglePin = (path: string) => { const normalizedPath = normalizePath(path) const fav = favorites.value.find(f => normalizePath(f.path) === normalizedPath) if (fav) { fav.pinnedAt = fav.pinnedAt ? undefined : Date.now() sortFavorites() saveFavorites() } } /** * 检查是否已置顶 */ const isPinned = (path: string): boolean => { const normalizedPath = normalizePath(path) const fav = favorites.value.find(f => normalizePath(f.path) === normalizedPath) return !!fav?.pinnedAt } /** * 更新收藏项路径(重命名时使用,保留置顶状态和添加时间) */ const updateFavoritePath = (oldPath: string, newName: string) => { const normalizedOld = normalizePath(oldPath) const fav = favorites.value.find(f => normalizePath(f.path) === normalizedOld) if (!fav) return const separator = getPathSeparator(oldPath) const parentPath = oldPath.substring( 0, Math.max(oldPath.lastIndexOf('\\'), oldPath.lastIndexOf('/')) ) fav.path = parentPath + separator + newName fav.name = newName saveFavorites() } // 拖拽方法 const onLongPressStart = (event: MouseEvent | TouchEvent, index: number) => { if (event instanceof MouseEvent && event.button !== 0) return if (!(event instanceof MouseEvent) && !(event instanceof TouchEvent)) return draggingState.value.pressedIndex = index draggingState.value.draggedIndex = index } const onLongPressCancel = () => { if (!draggingState.value.isDragging) { draggingState.value.pressedIndex = -1 draggingState.value.draggedIndex = -1 } } const onDragStart = (event: DragEvent, index: number) => { draggingState.value.isDragging = true draggingState.value.draggedIndex = index if (event.dataTransfer) { event.dataTransfer.effectAllowed = 'move' event.dataTransfer.setData('text/plain', index.toString()) } } const onDragOver = (event: DragEvent) => { event.preventDefault() if (event.dataTransfer) { event.dataTransfer.dropEffect = 'move' } } const onDrop = (event: DragEvent, targetIndex: number) => { event.preventDefault() const fromIndex = draggingState.value.draggedIndex if (fromIndex === targetIndex || fromIndex === -1) { resetDragging() return } const item = favorites.value.splice(fromIndex, 1)[0] favorites.value.splice(targetIndex, 0, item) saveFavorites() resetDragging() } const onDragEnd = () => { resetDragging() } const resetDragging = () => { draggingState.value.isDragging = false draggingState.value.draggedIndex = -1 draggingState.value.pressedIndex = -1 } // 组件挂载时加载收藏列表 loadFavorites() return { favorites, draggingState, addFavorite, removeFavorite, toggleFavorite, isFavorite, togglePin, isPinned, updateFavoritePath, onLongPressStart, onLongPressCancel, onDragStart, onDragOver, onDrop, onDragEnd, loadFavorites, saveFavorites, resetDragging } }