Skip to content

Commit eed53b7

Browse files
committed
refactor: 将当前类别状态从settingsSlice移动到新的uiSlice
1 parent 078c5c9 commit eed53b7

File tree

6 files changed

+109
-61
lines changed

6 files changed

+109
-61
lines changed

components/navigation/CategorySidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from '@dnd-kit/sortable';
2323
import { CSS } from '@dnd-kit/utilities';
2424
import { useAppDispatch, useAppSelector } from '@/store/hooks';
25-
import { setCurrentCategory } from '@/store/slices/settingsSlice';
25+
import { setCurrentCategory } from '@/store/slices/uiSlice';
2626
import {
2727
addCategory,
2828
deleteCategory,
@@ -135,7 +135,7 @@ const DraggableCategoryItem: React.FC<DraggableCategoryItemProps> = ({
135135
*/
136136
const CategorySidebarBase: React.FC<CategorySidebarProps> = ({ className, style }) => {
137137
const dispatch = useAppDispatch();
138-
const currentCategory = useAppSelector((state) => state.settings.currentCategory || '主页');
138+
const currentCategory = useAppSelector((state) => state.ui.currentCategory || '主页');
139139
const categories = useAppSelector((state) => state.categories.items);
140140
const links = useAppSelector((state) => state.links.items);
141141
const [mounted, setMounted] = useState(false);

components/navigation/LinkGrid.tsx

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { reorderLinks } from '@/store/slices/linksSlice';
2121
import { LinkCard } from './LinkCard';
2222
import { Link } from '@/types/link';
2323
import { showSuccess } from '@/utils/feedback';
24-
import { getDefaultCategoryName } from '@/services/defaultData';
2524

2625
interface LinkGridProps {
2726
onEdit?: (link: Link) => void;
@@ -36,15 +35,10 @@ interface LinkGridProps {
3635
* 支持根据分类和搜索状态过滤链接
3736
* 使用 React.memo 和 useMemo 优化性能
3837
*/
39-
const LinkGridBase: React.FC<LinkGridProps> = ({
40-
onEdit,
41-
onDelete,
42-
className,
43-
style
44-
}) => {
38+
const LinkGridBase: React.FC<LinkGridProps> = ({ onEdit, onDelete, className, style }) => {
4539
const dispatch = useAppDispatch();
4640
const links = useAppSelector((state) => state.links.items);
47-
const currentCategory = useAppSelector((state) => state.settings.currentCategory || getDefaultCategoryName());
41+
const currentCategory = useAppSelector((state) => state.ui.currentCategory || '主页');
4842
const searchQuery = useAppSelector((state) => state.search.query);
4943
const searchResults = useAppSelector((state) => state.search.results);
5044

@@ -97,20 +91,23 @@ const LinkGridBase: React.FC<LinkGridProps> = ({
9791
}, [links, currentCategory, searchQuery, searchResults]);
9892

9993
// 处理拖拽结束
100-
const handleDragEnd = useCallback((event: DragEndEvent) => {
101-
const { active, over } = event;
102-
103-
if (over && active.id !== over.id) {
104-
// 在所有链接中查找索引(不是 displayedLinks)
105-
const oldIndex = links.findIndex((link) => link.id === active.id);
106-
const newIndex = links.findIndex((link) => link.id === over.id);
107-
108-
if (oldIndex !== -1 && newIndex !== -1) {
109-
dispatch(reorderLinks({ fromIndex: oldIndex, toIndex: newIndex }));
110-
showSuccess('链接排序已更新');
94+
const handleDragEnd = useCallback(
95+
(event: DragEndEvent) => {
96+
const { active, over } = event;
97+
98+
if (over && active.id !== over.id) {
99+
// 在所有链接中查找索引(不是 displayedLinks)
100+
const oldIndex = links.findIndex((link) => link.id === active.id);
101+
const newIndex = links.findIndex((link) => link.id === over.id);
102+
103+
if (oldIndex !== -1 && newIndex !== -1) {
104+
dispatch(reorderLinks({ fromIndex: oldIndex, toIndex: newIndex }));
105+
showSuccess('链接排序已更新');
106+
}
111107
}
112-
}
113-
}, [links, dispatch]);
108+
},
109+
[links, dispatch]
110+
);
114111

115112
// 空状态判断
116113
const isEmpty = displayedLinks.length === 0;
@@ -121,24 +118,34 @@ const LinkGridBase: React.FC<LinkGridProps> = ({
121118
if (isEmpty) {
122119
if (isSearchEmpty) {
123120
return (
124-
<div className={className} style={{ ...style, display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '400px' }}>
125-
<Empty
126-
description={
127-
<span>
128-
没有找到与 &quot;{searchQuery}&quot; 相关的链接
129-
</span>
130-
}
131-
/>
121+
<div
122+
className={className}
123+
style={{
124+
...style,
125+
display: 'flex',
126+
alignItems: 'center',
127+
justifyContent: 'center',
128+
minHeight: '400px',
129+
}}
130+
>
131+
<Empty description={<span>没有找到与 &quot;{searchQuery}&quot; 相关的链接</span>} />
132132
</div>
133133
);
134134
}
135135

136136
if (isCategoryEmpty) {
137137
return (
138-
<div className={className} style={{ ...style, display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '400px' }}>
139-
<Empty
140-
description={`${currentCategory}分类暂无链接`}
141-
/>
138+
<div
139+
className={className}
140+
style={{
141+
...style,
142+
display: 'flex',
143+
alignItems: 'center',
144+
justifyContent: 'center',
145+
minHeight: '400px',
146+
}}
147+
>
148+
<Empty description={`${currentCategory}分类暂无链接`} />
142149
</div>
143150
);
144151
}
@@ -159,11 +166,17 @@ const LinkGridBase: React.FC<LinkGridProps> = ({
159166
strategy={rectSortingStrategy}
160167
disabled={!isDraggingEnabled}
161168
>
162-
<div
163-
className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-6 4xl:grid-cols-7 5xl:grid-cols-8 6xl:grid-cols-9 7xl:grid-cols-10 gap-x-8 gap-y-6 p-4 sm:p-8 md:px-10 max-w-full ${className || ''}`}
169+
<div
170+
className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 3xl:grid-cols-6 4xl:grid-cols-7 5xl:grid-cols-8 6xl:grid-cols-9 7xl:grid-cols-10 gap-x-8 gap-y-6 p-4 sm:p-8 md:px-10 max-w-full ${
171+
className || ''
172+
}`}
164173
style={{ ...style, width: '100%', boxSizing: 'border-box' }}
165174
role="region"
166-
aria-label={searchQuery.trim() ? `搜索结果:${displayedLinks.length} 个链接` : `${currentCategory}分类:${displayedLinks.length} 个链接`}
175+
aria-label={
176+
searchQuery.trim()
177+
? `搜索结果:${displayedLinks.length} 个链接`
178+
: `${currentCategory}分类:${displayedLinks.length} 个链接`
179+
}
167180
>
168181
{displayedLinks.map((link) => (
169182
<LinkCard

store/index.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import linksReducer from './slices/linksSlice';
33
import searchReducer from './slices/searchSlice';
44
import settingsReducer from './slices/settingsSlice';
55
import categoriesReducer from './slices/categoriesSlice';
6+
import uiReducer from './slices/uiSlice';
67
import { storageService } from '@/services/storage';
78

89
/**
@@ -12,10 +13,10 @@ import { storageService } from '@/services/storage';
1213
*/
1314
const localStorageSyncMiddleware: Middleware = (store) => (next) => (action) => {
1415
const result = next(action);
15-
16+
1617
// 获取 action 的类型
1718
const actionType = (action as { type?: string }).type || '';
18-
19+
1920
// 如果是 links 相关的 action,同步 links 数据
2021
if (typeof actionType === 'string' && actionType.startsWith('links/')) {
2122
const state = store.getState();
@@ -28,7 +29,7 @@ const localStorageSyncMiddleware: Middleware = (store) => (next) => (action) =>
2829
console.error('Failed to sync links to LocalStorage:', error);
2930
}
3031
}
31-
32+
3233
// 如果是 categories 相关的 action,同步 categories 数据
3334
if (typeof actionType === 'string' && actionType.startsWith('categories/')) {
3435
const state = store.getState();
@@ -38,7 +39,7 @@ const localStorageSyncMiddleware: Middleware = (store) => (next) => (action) =>
3839
console.error('Failed to sync categories to LocalStorage:', error);
3940
}
4041
}
41-
42+
4243
// 如果是 settings 相关的 action,同步 settings 数据
4344
if (typeof actionType === 'string' && actionType.startsWith('settings/')) {
4445
const state = store.getState();
@@ -53,7 +54,7 @@ const localStorageSyncMiddleware: Middleware = (store) => (next) => (action) =>
5354
console.error('Failed to sync settings to LocalStorage:', error);
5455
}
5556
}
56-
57+
5758
return result;
5859
};
5960

@@ -66,6 +67,7 @@ export const store = configureStore({
6667
search: searchReducer,
6768
settings: settingsReducer,
6869
categories: categoriesReducer,
70+
ui: uiReducer,
6971
},
7072
middleware: (getDefaultMiddleware) =>
7173
getDefaultMiddleware({

store/slices/settingsSlice.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
22
import { Settings, ThemeMode, LayoutMode, UpdateSettingsInput } from '@/types';
3-
import { getDefaultCategoryName } from '@/services/defaultData';
43

54
/**
65
* Settings 状态接口
@@ -19,7 +18,6 @@ const defaultSettings: Settings = {
1918
theme: 'system',
2019
searchEngine: 'google',
2120
layout: 'grid',
22-
currentCategory: getDefaultCategoryName(),
2321
showDescription: true,
2422
gridColumns: 6,
2523
};
@@ -65,14 +63,6 @@ const settingsSlice = createSlice({
6563
state.error = null;
6664
},
6765

68-
/**
69-
* 设置当前分类
70-
*/
71-
setCurrentCategory: (state, action: PayloadAction<string>) => {
72-
state.currentCategory = action.payload;
73-
state.error = null;
74-
},
75-
7666
/**
7767
* 设置是否显示描述
7868
*/
@@ -148,7 +138,6 @@ export const {
148138
setTheme,
149139
setSearchEngine,
150140
setLayout,
151-
setCurrentCategory,
152141
setShowDescription,
153142
setGridColumns,
154143
updateSettings,

store/slices/uiSlice.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2+
import { getDefaultCategoryName } from '@/services/defaultData';
3+
4+
/**
5+
* UI 状态接口
6+
* 用于管理非持久化的 UI 状态
7+
*/
8+
interface UiState {
9+
/** 当前选中的分类 */
10+
currentCategory: string;
11+
}
12+
13+
/**
14+
* 初始状态
15+
*/
16+
const initialState: UiState = {
17+
currentCategory: getDefaultCategoryName(),
18+
};
19+
20+
/**
21+
* UI Slice
22+
* 管理 UI 相关的临时状态
23+
*/
24+
const uiSlice = createSlice({
25+
name: 'ui',
26+
initialState,
27+
reducers: {
28+
/**
29+
* 设置当前分类
30+
*/
31+
setCurrentCategory: (state, action: PayloadAction<string>) => {
32+
state.currentCategory = action.payload;
33+
},
34+
},
35+
});
36+
37+
/**
38+
* 导出 actions
39+
*/
40+
export const {
41+
setCurrentCategory,
42+
} = uiSlice.actions;
43+
44+
/**
45+
* 导出 reducer
46+
*/
47+
export default uiSlice.reducer;

types/settings.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,16 @@ export type LayoutMode = 'grid' | 'list';
1515
export interface Settings {
1616
/** 主题模式 */
1717
theme: ThemeMode;
18-
18+
1919
/** 当前搜索引擎 ID */
2020
searchEngine: string;
21-
21+
2222
/** 布局模式 */
2323
layout: LayoutMode;
24-
25-
/** 当前选中的分类 */
26-
currentCategory?: string;
27-
24+
2825
/** 是否显示描述 */
2926
showDescription?: boolean;
30-
27+
3128
/** 每行显示的卡片数量(仅在 grid 模式下) */
3229
gridColumns?: number;
3330
}

0 commit comments

Comments
 (0)