- 简化计算属性,删除重复代码 - 优化文件扩展名获取逻辑 - 新增文件工具函数库 fileHelpers.js - 增强 CodeEditor 语法高亮(支持 30+ 语言) - 修复 Office 文档文件服务器访问权限 - 添加特殊文件名支持(Dockerfile、Makefile 等)
321 lines
8.3 KiB
JavaScript
321 lines
8.3 KiB
JavaScript
/**
|
||
* 收藏夹管理 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')
|
||
*
|
||
* // 在模板中使用
|
||
* <a-button @click="toggleFavorite(file)">
|
||
* <icon-star-fill v-if="isFavorite(file.path)" />
|
||
* <icon-star v-else />
|
||
* </a-button>
|
||
*/
|
||
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<boolean>} 是否成功打开
|
||
*/
|
||
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<Array>} 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
|
||
}
|