/** * 收藏夹管理 composable * * @module composables/useFavoriteFiles * @description 封装收藏夹的增删改查逻辑,支持持久化存储 */ import { ref, onMounted } from 'vue' import { Message } from '@arco-design/web-vue' import { useLocalStorage } from './useLocalStorage' /** * 收藏夹 composable * @param {string} storageKey - localStorage 键名 * @param {Object} [options] - 配置选项 * @param {number} [options.maxLength=50] - 最大收藏数量 * @param {Function} [options.onAdd] - 添加收藏回调 * @param {Function} [options.onRemove] - 移除收藏回调 * @returns {UseFavoriteFilesReturn} 收藏夹操作API * * @example * const { * favoriteFiles, * isFavorite, * toggleFavorite, * removeFavorite, * clearAll * } = useFavoriteFiles('app-favorites') * * // 在模板中使用 * * * * */ export function useFavoriteFiles(storageKey, options = {}) { const { maxLength = 50, onAdd = () => {}, onRemove = () => {}, } = options // 使用 localStorage composable 管理收藏列表 const { storedValue: favoriteFiles, load, save } = useLocalStorage( storageKey, [] ) /** * 判断文件/目录是否已收藏 * @param {string} path - 文件/目录路径 * @returns {boolean} 是否已收藏 */ const isFavorite = (path) => { if (!path || !Array.isArray(favoriteFiles.value)) { return false } return favoriteFiles.value.some(fav => fav.path === path) } /** * 排序收藏列表(按创建时间倒序,最新的在上面) */ const sortFavorites = () => { if (!Array.isArray(favoriteFiles.value)) { return } favoriteFiles.value.sort((a, b) => { const timeA = a.created_at || 0 const timeB = b.created_at || 0 return timeB - timeA // 倒序:最新的在上面 }) } /** * 切换收藏状态 * @param {Object} item - 文件/目录信息 * @param {string} item.path - 路径 * @param {string} item.name - 名称 * @param {boolean} item.is_dir - 是否为目录 * @returns {boolean} 操作后是否为收藏状态 */ const toggleFavorite = (item) => { if (!item || !item.path) { Message.warning('无效的文件信息') return false } const index = favoriteFiles.value.findIndex(fav => fav.path === item.path) if (index > -1) { // 已收藏,执行取消收藏 favoriteFiles.value.splice(index, 1) save(favoriteFiles.value) // 直接保存,不重新排序 onRemove(item) Message.info(`已取消收藏: ${item.name}`) return false } else { // 未收藏,执行添加收藏 if (favoriteFiles.value.length >= maxLength) { Message.warning(`收藏夹已满,最多只能收藏 ${maxLength} 项`) return false } favoriteFiles.value.push({ path: item.path, name: item.name, is_dir: item.is_dir || false, created_at: Date.now(), // 添加时间戳(用于 getSortedFavorites) }) save(favoriteFiles.value) // 直接保存,不重新排序(新项目添加到末尾) onAdd(item) Message.success(`已收藏: ${item.name}`) return true } } /** * 移除收藏 * @param {string} path - 文件/目录路径 * @returns {boolean} 是否成功移除 */ const removeFavorite = (path) => { if (!path) { Message.warning('请提供有效的路径') return false } const index = favoriteFiles.value.findIndex(fav => fav.path === path) if (index === -1) { Message.warning('该路径不在收藏夹中') return false } const item = favoriteFiles.value[index] favoriteFiles.value.splice(index, 1) save(favoriteFiles.value) // 直接保存,不重新排序 onRemove(item) Message.info(`已取消收藏: ${item.name}`) return true } /** * 打开收藏的文件/目录 * @param {string} path - 文件/目录路径 * @param {Function} onOpen - 打开回调函数 * @returns {Promise} 是否成功打开 */ const openFavorite = async (path, onOpen) => { if (!path || !onOpen) { return false } const item = favoriteFiles.value.find(fav => fav.path === path) if (!item) { Message.warning('收藏项不存在') return false } return await onOpen(item) } /** * 清空所有收藏 * @param {boolean} [confirm=true] - 是否需要确认 * @returns {boolean} 是否成功清空 */ const clearAll = (confirm = true) => { const executeClear = () => { const count = favoriteFiles.value.length favoriteFiles.value = [] save([]) Message.success(`已清空 ${count} 个收藏项`) return true } if (!confirm) { return executeClear() } // 使用原生 confirm(简单场景) if (window.confirm(`确定要清空所有 ${favoriteFiles.value.length} 个收藏项吗?`)) { return executeClear() } return false } /** * 获取收藏列表(按创建时间排序) * @param {string} [order='desc'] - 排序方式:'asc'或'desc' * @returns {Array} 排序后的收藏列表 */ const getSortedFavorites = (order = 'desc') => { const sorted = [...favoriteFiles.value] sorted.sort((a, b) => { const timeA = a.created_at || 0 const timeB = b.created_at || 0 return order === 'desc' ? timeB - timeA : timeA - timeB }) return sorted } /** * 按名称搜索收藏 * @param {string} keyword - 搜索关键词 * @returns {Array} 匹配的收藏列表 */ const searchFavorites = (keyword) => { if (!keyword || !keyword.trim()) { return favoriteFiles.value } const lowerKeyword = keyword.toLowerCase().trim() return favoriteFiles.value.filter(fav => fav.name.toLowerCase().includes(lowerKeyword) || fav.path.toLowerCase().includes(lowerKeyword) ) } /** * 重新排序收藏列表(拖拽排序) * @param {number} fromIndex - 源索引 * @param {number} toIndex - 目标索引 * @returns {boolean} 是否成功重排序 */ const reorderFavorites = (fromIndex, toIndex) => { if (!Array.isArray(favoriteFiles.value)) { return false } if (fromIndex < 0 || fromIndex >= favoriteFiles.value.length || toIndex < 0 || toIndex >= favoriteFiles.value.length) { return false } if (fromIndex === toIndex) { return false } // 移动数组元素 const [movedItem] = favoriteFiles.value.splice(fromIndex, 1) favoriteFiles.value.splice(toIndex, 0, movedItem) // 保存新顺序 save(favoriteFiles.value) return true } // 组件挂载时加载数据(不自动排序,保持用户拖拽的顺序) onMounted(() => { load() }) return { // 状态 favoriteFiles, // 方法 isFavorite, toggleFavorite, removeFavorite, openFavorite, clearAll, getSortedFavorites, searchFavorites, sortFavorites, reorderFavorites, load, save, } } /** * @typedef {Object} UseFavoriteFilesReturn * @property {Ref} favoriteFiles - 收藏列表(自动按时间倒序排列) * @property {Function} isFavorite - 判断是否已收藏 * @property {Function} toggleFavorite - 切换收藏状态 * @property {Function} removeFavorite - 移除收藏 * @property {Function} openFavorite - 打开收藏项 * @property {Function} clearAll - 清空所有收藏 * @property {Function} getSortedFavorites - 获取排序后的列表 * @property {Function} searchFavorites - 搜索收藏 * @property {Function} sortFavorites - 手动排序收藏列表 * @property {Function} reorderFavorites - 拖拽重新排序 * @property {Function} load - 手动加载数据 * @property {Function} save - 手动保存数据 */ /** * 创建多个收藏夹管理实例 * @param {Object} config - 配置对象 * @returns {Object} 收藏夹管理实例集合 * * @example * const { * filesystemFavs, * deviceTestFavs * } = createMultipleFavorites({ * filesystem: 'app-filesystem-favorites', * deviceTest: 'app-device-test-favorites' * }) */ export function createMultipleFavorites(config) { const result = {} Object.keys(config).forEach(key => { result[key] = useFavoriteFiles(config[key]) }) return result }