feat: 优化路径收藏模态框加载逻辑
This commit is contained in:
@@ -9,6 +9,7 @@ import { useAppearanceStore } from './stores/appearance.store';
|
|||||||
import { useLayoutStore } from './stores/layout.store';
|
import { useLayoutStore } from './stores/layout.store';
|
||||||
import { useFocusSwitcherStore } from './stores/focusSwitcher.store';
|
import { useFocusSwitcherStore } from './stores/focusSwitcher.store';
|
||||||
import { useSessionStore } from './stores/session.store';
|
import { useSessionStore } from './stores/session.store';
|
||||||
|
import { useFavoritePathsStore } from './stores/favoritePaths.store';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import UINotificationDisplay from './components/UINotificationDisplay.vue';
|
import UINotificationDisplay from './components/UINotificationDisplay.vue';
|
||||||
import FileEditorOverlay from './components/FileEditorOverlay.vue';
|
import FileEditorOverlay from './components/FileEditorOverlay.vue';
|
||||||
@@ -24,6 +25,7 @@ const appearanceStore = useAppearanceStore();
|
|||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化焦点切换 Store +++
|
||||||
const sessionStore = useSessionStore(); // +++ 实例化 Session Store +++
|
const sessionStore = useSessionStore(); // +++ 实例化 Session Store +++
|
||||||
|
const favoritePathsStore = useFavoritePathsStore(); // +++ 实例化 favoritePathsStore +++
|
||||||
const { isAuthenticated } = storeToRefs(authStore);
|
const { isAuthenticated } = storeToRefs(authStore);
|
||||||
const { showPopupFileEditorBoolean } = storeToRefs(settingsStore);
|
const { showPopupFileEditorBoolean } = storeToRefs(settingsStore);
|
||||||
const { isStyleCustomizerVisible } = storeToRefs(appearanceStore);
|
const { isStyleCustomizerVisible } = storeToRefs(appearanceStore);
|
||||||
@@ -78,6 +80,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
// +++ 加载 Header 可见性状态 +++
|
// +++ 加载 Header 可见性状态 +++
|
||||||
layoutStore.loadHeaderVisibility();
|
layoutStore.loadHeaderVisibility();
|
||||||
|
favoritePathsStore.initializeFavoritePaths(t); // +++ 初始化收藏路径数据 +++
|
||||||
});
|
});
|
||||||
|
|
||||||
// +++ 卸载钩子以移除监听器 +++
|
// +++ 卸载钩子以移除监听器 +++
|
||||||
|
|||||||
@@ -163,7 +163,6 @@ const handleClickOutside = (event: MouseEvent) => {
|
|||||||
|
|
||||||
watch(() => props.isVisible, (newValue: boolean) => {
|
watch(() => props.isVisible, (newValue: boolean) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
favoritePathsStore.fetchFavoritePaths(t);
|
|
||||||
searchTerm.value = '';
|
searchTerm.value = '';
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
nextTick(() => { // Ensure DOM is ready for measurements
|
nextTick(() => { // Ensure DOM is ready for measurements
|
||||||
@@ -178,8 +177,12 @@ watch(() => props.isVisible, (newValue: boolean) => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isVisible) {
|
if (props.isVisible) {
|
||||||
favoritePathsStore.fetchFavoritePaths(t);
|
searchTerm.value = '';
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
nextTick(() => {
|
||||||
|
updatePosition();
|
||||||
|
window.addEventListener('resize', updatePosition);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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<FavoritePathItem[]>('/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<FavoritePathItem, 'id' | 'createdAt' | 'updatedAt' | 'last_used_at'>, 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<Omit<FavoritePathItem, 'id' | 'createdAt' | 'updatedAt' | 'last_used_at'>>, 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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user