完成Work组件的界面和一些小修改
> 1. 修改优化路由守卫
> 2. 去除拦截器中的调试信息
> 3. 修改头部导航条下拉菜单的样式增加图标。
> 4. work组件现在使用mock数据

Change-Id: Ic602a35bb02e645a0d5253c5cbd12a68d70bfb33
diff --git a/src/feature/work/workSlice.ts b/src/feature/work/workSlice.ts
new file mode 100644
index 0000000..6b2c12c
--- /dev/null
+++ b/src/feature/work/workSlice.ts
@@ -0,0 +1,469 @@
+import { createSlice, createAsyncThunk, type PayloadAction } from '@reduxjs/toolkit';
+import type { ArtworkData, Comment } from './types';
+import type { RootState } from '../../store/store';
+import { getArtworkById, getCommentsForArtwork } from './mockData';
+
+// ==================== 类型定义 ====================
+interface WorkState {
+    currentArtwork: ArtworkData | null;
+    loading: {
+        artwork: boolean;
+        comments: boolean;
+        addComment: boolean;
+        updateArtwork: boolean;
+        deleteComment: boolean;
+    };
+    error: {
+        artwork: string | null;
+        comments: string | null;
+        addComment: string | null;
+        updateArtwork: string | null;
+        deleteComment: string | null;
+    };
+    comments: {
+        list: Comment[];
+        total: number;
+        current: number;
+        pageSize: number;
+    };
+}
+
+interface FetchCommentsParams {
+    workId: string;
+    page: number;
+    pageSize: number;
+}
+
+interface AddCommentParams {
+    workId: string;
+    content: string;
+    parentId?: string;
+}
+
+interface UpdateArtworkParams {
+    workId: string;
+    updates: Partial<ArtworkData>;
+}
+
+interface DeleteCommentParams {
+    workId: string;
+    commentId: string;
+}
+
+interface SetCommentsPageParams {
+    current: number;
+    pageSize: number;
+}
+
+// ==================== 初始状态 ====================
+const initialState: WorkState = {
+    currentArtwork: null,
+    loading: {
+        artwork: false,
+        comments: false,
+        addComment: false,
+        updateArtwork: false,
+        deleteComment: false,
+    },
+    error: {
+        artwork: null,
+        comments: null,
+        addComment: null,
+        updateArtwork: null,
+        deleteComment: null,
+    },
+    comments: {
+        list: [],
+        total: 0,
+        current: 1,
+        pageSize: 5,
+    },
+};
+
+// ==================== Mock 工具函数 ====================
+
+// 模拟网络延迟
+const mockDelay = (ms: number = 800): Promise<void> => 
+    new Promise(resolve => setTimeout(resolve, ms));
+
+// 生成新评论ID
+const generateCommentId = (): string => {
+    return `comment_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+};
+
+// 生成新评论数据
+const createNewComment = (content: string): Comment => {
+    return {
+        id: generateCommentId(),
+        content,
+        author: '当前用户', // 实际应用中从用户状态获取
+        authorId: 'current_user_id',
+        createdAt: new Date().toLocaleString('zh-CN'),
+        child: [],
+    };
+};
+
+// 递归删除评论
+const removeCommentById = (comments: Comment[], targetId: string): Comment[] => {
+    return comments.filter(comment => {
+        if (comment.id === targetId) {
+            return false;
+        }
+        if (comment.child.length > 0) {
+            comment.child = removeCommentById(comment.child, targetId);
+        }
+        return true;
+    });
+};
+
+// 递归添加回复评论
+const addReplyToComment = (comments: Comment[], parentId: string, newComment: Comment): Comment[] => {
+    return comments.map(comment => {
+        if (comment.id === parentId) {
+            return {
+                ...comment,
+                child: [...comment.child, newComment]
+            };
+        }
+        if (comment.child.length > 0) {
+            return {
+                ...comment,
+                child: addReplyToComment(comment.child, parentId, newComment)
+            };
+        }
+        return comment;
+    });
+};
+
+// 分页处理评论
+const paginateComments = (comments: Comment[], page: number, pageSize: number): Comment[] => {
+    const startIndex = (page - 1) * pageSize;
+    const endIndex = startIndex + pageSize;
+    return comments.slice(startIndex, endIndex);
+};
+
+// ==================== 异步 Actions ====================
+
+// 获取作品详情
+export const fetchArtworkDetail = createAsyncThunk<
+    ArtworkData,
+    string,
+    { rejectValue: string }
+>(
+    'work/fetchArtworkDetail',
+    async (workId: string, { rejectWithValue }) => {
+        try {
+            await mockDelay(600); // 模拟网络延迟
+            
+            const artwork = getArtworkById(workId);
+            
+            if (!artwork) {
+                throw new Error(`作品 ${workId} 不存在`);
+            }
+            
+            return artwork;
+        } catch (error) {
+            const message = error instanceof Error ? error.message : '获取作品详情失败';
+            return rejectWithValue(message);
+        }
+    }
+);
+
+// 获取评论列表
+export const fetchComments = createAsyncThunk<
+    { comments: Comment[]; total: number },
+    FetchCommentsParams,
+    { rejectValue: string }
+>(
+    'work/fetchComments',
+    async ({ workId, page, pageSize }, { rejectWithValue }) => {
+        try {
+            await mockDelay(400); // 模拟网络延迟
+            
+            const allComments = getCommentsForArtwork(workId);
+            const paginatedComments = paginateComments(allComments, page, pageSize);
+            
+            return {
+                comments: paginatedComments,
+                total: allComments.length
+            };
+        } catch (error) {
+            const message = error instanceof Error ? error.message : '获取评论失败';
+            return rejectWithValue(message);
+        }
+    }
+);
+
+// 添加评论
+export const addComment = createAsyncThunk<
+    Comment,
+    AddCommentParams,
+    { rejectValue: string }
+>(
+    'work/addComment',
+    async ({ workId, content }, { rejectWithValue }) => {
+        try {
+            await mockDelay(500); // 模拟网络延迟
+            
+            // 验证作品是否存在
+            const artwork = getArtworkById(workId);
+            if (!artwork) {
+                throw new Error('作品不存在');
+            }
+            
+            // 创建新评论
+            const newComment = createNewComment(content);
+            
+            // 模拟服务器返回完整的评论数据
+            return newComment;
+        } catch (error) {
+            const message = error instanceof Error ? error.message : '添加评论失败';
+            return rejectWithValue(message);
+        }
+    }
+);
+
+// 更新作品信息
+export const updateArtwork = createAsyncThunk<
+    ArtworkData,
+    UpdateArtworkParams,
+    { rejectValue: string }
+>(
+    'work/updateArtwork',
+    async ({ workId, updates }, { rejectWithValue }) => {
+        try {
+            await mockDelay(1000); // 模拟网络延迟
+            
+            const currentArtwork = getArtworkById(workId);
+            if (!currentArtwork) {
+                throw new Error('作品不存在');
+            }
+            
+            // 模拟文件上传处理
+            const processedUpdates = { ...updates };
+            
+            // 如果包含 blob URL,模拟转换为正式URL
+            if (updates.artworkCover && updates.artworkCover.startsWith('blob:')) {
+                // 模拟上传成功,生成新的图片URL
+                processedUpdates.artworkCover = `https://picsum.photos/300/400?random=${Date.now()}`;
+            }
+            
+            // 处理版本文件上传
+            if (updates.versionList) {
+                processedUpdates.versionList = updates.versionList.map(version => ({
+                    ...version,
+                    seedFile: version.seedFile.startsWith?.('blob:') 
+                        ? `magnet:?xt=urn:btih:updated_${Date.now()}&dn=${version.version}.zip`
+                        : version.seedFile
+                }));
+            }
+            
+            // 合并更新后的数据
+            const updatedArtwork: ArtworkData = {
+                ...currentArtwork,
+                ...processedUpdates,
+                updatedAt: new Date().toISOString(),
+            };
+            
+            return updatedArtwork;
+        } catch (error) {
+            const message = error instanceof Error ? error.message : '更新作品失败';
+            return rejectWithValue(message);
+        }
+    }
+);
+
+// 删除评论
+export const deleteComment = createAsyncThunk<
+    string,
+    DeleteCommentParams,
+    { rejectValue: string }
+>(
+    'work/deleteComment',
+    async ({ workId, commentId }, { rejectWithValue }) => {
+        try {
+            await mockDelay(300); // 模拟网络延迟
+            
+            // 验证作品是否存在
+            const artwork = getArtworkById(workId);
+            if (!artwork) {
+                throw new Error('作品不存在');
+            }
+            
+            // 模拟删除成功
+            return commentId;
+        } catch (error) {
+            const message = error instanceof Error ? error.message : '删除评论失败';
+            return rejectWithValue(message);
+        }
+    }
+);
+
+// ==================== Slice 定义 ====================
+const workSlice = createSlice({
+    name: 'work',
+    initialState,
+    reducers: {
+        // 清除当前作品
+        clearCurrentArtwork: (state) => {
+            state.currentArtwork = null;
+            state.comments.list = [];
+            state.comments.total = 0;
+            state.comments.current = 1;
+            // 清除所有错误状态
+            Object.keys(state.error).forEach(key => {
+                state.error[key as keyof typeof state.error] = null;
+            });
+        },
+        
+        // 设置评论分页
+        setCommentsPage: (state, action: PayloadAction<SetCommentsPageParams>) => {
+            state.comments.current = action.payload.current;
+            state.comments.pageSize = action.payload.pageSize;
+        },
+        
+        // 清除特定错误
+        clearError: (state, action: PayloadAction<keyof WorkState['error']>) => {
+            state.error[action.payload] = null;
+        },
+        
+        // 清除所有错误
+        clearAllErrors: (state) => {
+            Object.keys(state.error).forEach(key => {
+                state.error[key as keyof typeof state.error] = null;
+            });
+        },
+    },
+    extraReducers: (builder) => {
+        // 获取作品详情
+        builder
+            .addCase(fetchArtworkDetail.pending, (state) => {
+                state.loading.artwork = true;
+                state.error.artwork = null;
+            })
+            .addCase(fetchArtworkDetail.fulfilled, (state, action) => {
+                state.loading.artwork = false;
+                state.currentArtwork = action.payload;
+                state.error.artwork = null;
+            })
+            .addCase(fetchArtworkDetail.rejected, (state, action) => {
+                state.loading.artwork = false;
+                state.error.artwork = action.payload || '获取作品详情失败';
+            });
+
+        // 获取评论列表
+        builder
+            .addCase(fetchComments.pending, (state) => {
+                state.loading.comments = true;
+                state.error.comments = null;
+            })
+            .addCase(fetchComments.fulfilled, (state, action) => {
+                state.loading.comments = false;
+                state.comments.list = action.payload.comments;
+                state.comments.total = action.payload.total;
+                state.error.comments = null;
+            })
+            .addCase(fetchComments.rejected, (state, action) => {
+                state.loading.comments = false;
+                state.error.comments = action.payload || '获取评论失败';
+            });
+
+        // 添加评论
+        builder
+            .addCase(addComment.pending, (state) => {
+                state.loading.addComment = true;
+                state.error.addComment = null;
+            })
+            .addCase(addComment.fulfilled, (state, action) => {
+                state.loading.addComment = false;
+                
+                const newComment = action.payload;
+                const { parentId } = action.meta.arg;
+                
+                if (parentId) {
+                    // 添加回复评论
+                    state.comments.list = addReplyToComment(state.comments.list, parentId, newComment);
+                } else {
+                    // 添加顶级评论
+                    state.comments.list.unshift(newComment);
+                    state.comments.total += 1;
+                }
+                
+                state.error.addComment = null;
+            })
+            .addCase(addComment.rejected, (state, action) => {
+                state.loading.addComment = false;
+                state.error.addComment = action.payload || '添加评论失败';
+            });
+
+        // 更新作品信息
+        builder
+            .addCase(updateArtwork.pending, (state) => {
+                state.loading.updateArtwork = true;
+                state.error.updateArtwork = null;
+            })
+            .addCase(updateArtwork.fulfilled, (state, action) => {
+                state.loading.updateArtwork = false;
+                state.currentArtwork = action.payload;
+                state.error.updateArtwork = null;
+            })
+            .addCase(updateArtwork.rejected, (state, action) => {
+                state.loading.updateArtwork = false;
+                state.error.updateArtwork = action.payload || '更新作品失败';
+            });
+
+        // 删除评论
+        builder
+            .addCase(deleteComment.pending, (state) => {
+                state.loading.deleteComment = true;
+                state.error.deleteComment = null;
+            })
+            .addCase(deleteComment.fulfilled, (state, action) => {
+                state.loading.deleteComment = false;
+                
+                // 从评论列表中移除已删除的评论
+                state.comments.list = removeCommentById(state.comments.list, action.payload);
+                state.comments.total = Math.max(0, state.comments.total - 1);
+                state.error.deleteComment = null;
+            })
+            .addCase(deleteComment.rejected, (state, action) => {
+                state.loading.deleteComment = false;
+                state.error.deleteComment = action.payload || '删除评论失败';
+            });
+    },
+});
+
+// ==================== Actions 导出 ====================
+export const { 
+    clearCurrentArtwork, 
+    setCommentsPage, 
+    clearError, 
+    clearAllErrors 
+} = workSlice.actions;
+
+// ==================== Selectors 导出 ====================
+export const selectCurrentArtwork = (state: RootState): ArtworkData | null => 
+    state.work.currentArtwork;
+
+export const selectWorkLoading = (state: RootState): WorkState['loading'] => 
+    state.work.loading;
+
+export const selectWorkError = (state: RootState): WorkState['error'] => 
+    state.work.error;
+
+export const selectComments = (state: RootState): WorkState['comments'] => 
+    state.work.comments;
+
+export const selectIsAuthor = (state: RootState): boolean => {
+    const currentUser = state.user;
+    const currentArtwork = state.work.currentArtwork;
+    
+    return Boolean(
+        currentUser?.userid && 
+        currentArtwork?.authorId &&
+        String(currentUser.userid) === String(currentArtwork.authorId)
+    );
+};
+
+// ==================== Reducer 导出 ====================
+export default workSlice.reducer;
\ No newline at end of file