diff --git a/packages/frontend/src/App.vue b/packages/frontend/src/App.vue index 814c8f2..ba8b6f6 100644 --- a/packages/frontend/src/App.vue +++ b/packages/frontend/src/App.vue @@ -9,6 +9,7 @@ import { useAppearanceStore } from './stores/appearance.store'; import { useLayoutStore } from './stores/layout.store'; import { useFocusSwitcherStore } from './stores/focusSwitcher.store'; import { useSessionStore } from './stores/session.store'; +import { useFavoritePathsStore } from './stores/favoritePaths.store'; import { storeToRefs } from 'pinia'; import UINotificationDisplay from './components/UINotificationDisplay.vue'; import FileEditorOverlay from './components/FileEditorOverlay.vue'; @@ -24,6 +25,7 @@ const appearanceStore = useAppearanceStore(); const layoutStore = useLayoutStore(); const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++ const sessionStore = useSessionStore(); // +++ 实例化 Session Store +++ +const favoritePathsStore = useFavoritePathsStore(); // +++ 实例化 favoritePathsStore +++ const { isAuthenticated } = storeToRefs(authStore); const { showPopupFileEditorBoolean } = storeToRefs(settingsStore); const { isStyleCustomizerVisible } = storeToRefs(appearanceStore); @@ -78,6 +80,7 @@ onMounted(() => { // +++ 加载 Header 可见性状态 +++ layoutStore.loadHeaderVisibility(); + favoritePathsStore.initializeFavoritePaths(t); // +++ 初始化收藏路径数据 +++ }); // +++ 卸载钩子以移除监听器 +++ diff --git a/packages/frontend/src/components/FavoritePathsModal.vue b/packages/frontend/src/components/FavoritePathsModal.vue index fa46fd8..41d96d9 100644 --- a/packages/frontend/src/components/FavoritePathsModal.vue +++ b/packages/frontend/src/components/FavoritePathsModal.vue @@ -163,7 +163,6 @@ const handleClickOutside = (event: MouseEvent) => { watch(() => props.isVisible, (newValue: boolean) => { if (newValue) { - favoritePathsStore.fetchFavoritePaths(t); searchTerm.value = ''; document.addEventListener('mousedown', handleClickOutside); nextTick(() => { // Ensure DOM is ready for measurements @@ -178,8 +177,12 @@ watch(() => props.isVisible, (newValue: boolean) => { onMounted(() => { if (props.isVisible) { - favoritePathsStore.fetchFavoritePaths(t); + searchTerm.value = ''; document.addEventListener('mousedown', handleClickOutside); + nextTick(() => { + updatePosition(); + window.addEventListener('resize', updatePosition); + }); } }); diff --git a/packages/frontend/src/stores/favoritePaths.store.ts b/packages/frontend/src/stores/favoritePaths.store.ts deleted file mode 100644 index c407f79..0000000 --- a/packages/frontend/src/stores/favoritePaths.store.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { defineStore } from 'pinia'; -import apiClient from '../utils/apiClient'; -import { useUiNotificationsStore } from './uiNotifications.store'; - -// TODO: Define these types more precisely based on API response -export type FavoritePathSortType = 'name' | 'last_used_at'; - -export interface FavoritePathItem { - id: string; - path: string; - name?: string; - last_used_at?: number | null; // Added last_used_at - // Add other relevant fields from the API if any - createdAt?: string; - updatedAt?: string; -} - -export interface FavoritePathsState { - favoritePaths: FavoritePathItem[]; - isLoading: boolean; - error: string | null; - searchTerm: string; - currentSortBy: FavoritePathSortType; // Added currentSortBy -} - -export const useFavoritePathsStore = defineStore('favoritePaths', { - state: (): FavoritePathsState => { - const savedSortBy = localStorage.getItem('favoritePathSortBy') as FavoritePathSortType | null; - return { - favoritePaths: [], - isLoading: false, - error: null, - searchTerm: '', - currentSortBy: savedSortBy || 'name', // Initialize from localStorage or default to 'name' - }; - }, - getters: { - // The filteredFavoritePaths getter will now operate on the already sorted list - filteredFavoritePaths(state): FavoritePathItem[] { - if (!state.searchTerm) { - return state.favoritePaths; - } - const lowerCaseSearchTerm = state.searchTerm.toLowerCase(); - // Note: state.favoritePaths is now always sorted by this.currentSortBy - return state.favoritePaths.filter(fav => - fav.path.toLowerCase().includes(lowerCaseSearchTerm) || - (fav.name && fav.name.toLowerCase().includes(lowerCaseSearchTerm)) - ); - }, - getFavoritePathById(state): (id: string) => FavoritePathItem | undefined { - return (id) => state.favoritePaths.find(fav => fav.id === id); - } - }, - actions: { - _sortFavoritePaths() { - this.favoritePaths.sort((a, b) => { - if (this.currentSortBy === 'name') { - const nameA = a.name?.toLowerCase() || a.path.toLowerCase(); - const nameB = b.name?.toLowerCase() || b.path.toLowerCase(); - if (nameA < nameB) return -1; - if (nameA > nameB) return 1; - return 0; - } else if (this.currentSortBy === 'last_used_at') { - // Sort by last_used_at descending, nulls/undefined last - const timeA = a.last_used_at ?? 0; - const timeB = b.last_used_at ?? 0; - return timeB - timeA; // Descending - } - return 0; - }); - }, - setSearchTerm(term: string) { - this.searchTerm = term; - }, - async fetchFavoritePaths(t: (key: string, defaultMessage: string) => string) { - this.isLoading = true; - this.error = null; - const notificationsStore = useUiNotificationsStore(); - try { - // Fetch all paths, sorting will be done locally - const response = await apiClient.get('/favorite-paths'); - this.favoritePaths = response.data; - this._sortFavoritePaths(); // Sort locally after fetching - } catch (err: any) { - this.error = err.message || 'Failed to fetch favorite paths'; - console.error('Error fetching favorite paths:', err); - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.fetchError', 'Failed to load favorite paths.'), - type: 'error', - }); - } finally { - this.isLoading = false; - } - }, - setSortBy(sortBy: FavoritePathSortType) { - this.currentSortBy = sortBy; - localStorage.setItem('favoritePathSortBy', sortBy); - this._sortFavoritePaths(); // Re-sort locally - }, - async markPathAsUsed(pathId: string, t: (key: string, defaultMessage: string) => string) { - const notificationsStore = useUiNotificationsStore(); - try { - const response = await apiClient.put<{ message: string, favoritePath: FavoritePathItem }>(`/favorite-paths/${pathId}/update-last-used`); - const updatedPath = response.data.favoritePath; - if (updatedPath) { - const index = this.favoritePaths.findIndex(p => p.id === pathId); - if (index !== -1) { - this.favoritePaths[index] = updatedPath; - } else { - // Path not found locally, might happen if list is stale. Add it. - this.favoritePaths.push(updatedPath); - } - this._sortFavoritePaths(); // Re-sort after updating - } else { - // Fallback to re-fetch if updated item isn't returned as expected - console.warn('markPathAsUsed did not receive updated path, re-fetching list.'); - await this.fetchFavoritePaths(t); - } - } catch (err: any) { - console.error(`Error marking path ${pathId} as used:`, err); - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.markAsUsedError', 'Failed to mark path as used.'), - type: 'error', - }); - } - }, - async addFavoritePath(newPathData: Omit, t: (key: string, defaultMessage: string) => string) { - this.isLoading = true; - this.error = null; - const notificationsStore = useUiNotificationsStore(); - try { - const response = await apiClient.post<{ message: string, favoritePath: FavoritePathItem }>('/favorite-paths', newPathData); - this.favoritePaths.push(response.data.favoritePath); - this._sortFavoritePaths(); // Sort after adding - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.addSuccess', 'Favorite path added successfully.'), - type: 'success', - }); - } catch (err: any) { - this.error = err.message || 'Failed to add favorite path'; - console.error('Error adding favorite path:', err); - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.addError', 'Failed to add favorite path.'), - type: 'error', - }); - throw err; // Re-throw to allow form to handle error - } finally { - this.isLoading = false; - } - }, - async updateFavoritePath(id: string, updatedPathData: Partial>, t: (key: string, defaultMessage: string) => string) { - this.isLoading = true; - this.error = null; - const notificationsStore = useUiNotificationsStore(); - try { - const response = await apiClient.put<{ message: string, favoritePath: FavoritePathItem }>(`/favorite-paths/${id}`, updatedPathData); - const index = this.favoritePaths.findIndex(fav => fav.id === id); - if (index !== -1) { - this.favoritePaths[index] = response.data.favoritePath; - this._sortFavoritePaths(); // Sort after updating - } - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.updateSuccess', 'Favorite path updated successfully.'), - type: 'success', - }); - } catch (err: any) { - this.error = err.message || 'Failed to update favorite path'; - console.error('Error updating favorite path:', err); - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.updateError', 'Failed to update favorite path.'), - type: 'error', - }); - throw err; // Re-throw to allow form to handle error - } finally { - this.isLoading = false; - } - }, - async deleteFavoritePath(id: string, t: (key: string, defaultMessage: string) => string) { - this.isLoading = true; - this.error = null; - const notificationsStore = useUiNotificationsStore(); - try { - await apiClient.delete(`/favorite-paths/${id}`); - this.favoritePaths = this.favoritePaths.filter(fav => fav.id !== id); - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.deleteSuccess', 'Favorite path deleted successfully.'), - type: 'success', - }); - } catch (err: any) { - this.error = err.message || 'Failed to delete favorite path'; - console.error('Error deleting favorite path:', err); - notificationsStore.addNotification({ - message: t('favoritePaths.notifications.deleteError', 'Failed to delete favorite path.'), - type: 'error', - }); - } finally { - this.isLoading = false; - } - }, - }, -}); \ No newline at end of file