- 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绑定
257 lines
6.4 KiB
TypeScript
257 lines
6.4 KiB
TypeScript
/**
|
||
* 收藏夹管理 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
|
||
}
|
||
}
|