Private
Public Access
1
0
Files
u-desk/web/src/components/FileSystem/composables/useFavorites.ts
绝尘 efc042fcd3 优化:CSV编辑模式/PDF导出重构/收藏夹bug修复/移除useLocalStorage
- FileEditorPanel: CSV新增预览/编辑切换、PDF导出;提取openPrintWindow公共函数
- useFavorites: 修复find回调中fav变量遮蔽bug(f.path)、sort改为副本排序
- useFavoriteFiles/DeviceTest: 移除useLocalStorage抽象层,直接管理localStorage
- system.ts: createDir/createFile签名改为(parent, name)两参数拼接
- useFileOperations: createNewFile移除无用content参数
- 清理OpenClaw相关Wails绑定
2026-04-07 11:58:42 +08:00

257 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 收藏夹管理 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<FavoriteFile[]>([])
// 拖拽状态
const draggingState = ref<DraggingState>({
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
}
}