|
|
@@ -1,10 +1,15 @@
|
|
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
|
+import { createAsyncThunk } from '@reduxjs/toolkit';
|
|
|
+import { message } from 'antd';
|
|
|
+import { fetchMarks, createMark, deleteMark, Mark } from '../../API/mark';
|
|
|
|
|
|
interface MarkPanelState {
|
|
|
- customMarks: string[]; // 用户添加的自定义标记列表
|
|
|
+ customMarks: Mark[]; // 用户添加的自定义标记列表(包含ID)
|
|
|
selectedMark: string; // 当前选中的标记文本
|
|
|
inputText: string; // 输入框中的文本
|
|
|
selectedViewportIds: string[]; // 选中的视口ID
|
|
|
+ loading: boolean; // 加载状态
|
|
|
+ error: string | null; // 错误信息
|
|
|
}
|
|
|
|
|
|
const initialState: MarkPanelState = {
|
|
|
@@ -12,24 +17,65 @@ const initialState: MarkPanelState = {
|
|
|
selectedMark: '',
|
|
|
inputText: '',
|
|
|
selectedViewportIds: [],
|
|
|
+ loading: false,
|
|
|
+ error: null,
|
|
|
};
|
|
|
|
|
|
+// 异步 thunk
|
|
|
+export const fetchMarksAsync = createAsyncThunk(
|
|
|
+ 'markPanel/fetchMarks',
|
|
|
+ async (scope: 'all' | 'mine' = 'all', { rejectWithValue }) => {
|
|
|
+ try {
|
|
|
+ const response = await fetchMarks(scope);
|
|
|
+ if (response.code === '0x000000') {
|
|
|
+ return response.data.marks;
|
|
|
+ } else {
|
|
|
+ return rejectWithValue(response.description || '获取标记列表失败');
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ return rejectWithValue(error.message || '网络错误');
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+export const addMarkAsync = createAsyncThunk(
|
|
|
+ 'markPanel/addMark',
|
|
|
+ async (markText: string, { rejectWithValue }) => {
|
|
|
+ try {
|
|
|
+ const response = await createMark(markText);
|
|
|
+ if (response.code === '0x000000') {
|
|
|
+ message.success('标记添加成功');
|
|
|
+ return { mark: markText };
|
|
|
+ } else {
|
|
|
+ return rejectWithValue(response.description || '添加标记失败');
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ return rejectWithValue(error.message || '网络错误');
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+export const deleteMarkAsync = createAsyncThunk(
|
|
|
+ 'markPanel/deleteMark',
|
|
|
+ async (markId: number, { rejectWithValue }) => {
|
|
|
+ try {
|
|
|
+ const response = await deleteMark(markId);
|
|
|
+ if (response.code === '0x000000') {
|
|
|
+ message.success('标记删除成功');
|
|
|
+ return markId;
|
|
|
+ } else {
|
|
|
+ return rejectWithValue(response.description || '删除标记失败');
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ return rejectWithValue(error.message || '网络错误');
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
const markPanelSlice = createSlice({
|
|
|
name: 'markPanel',
|
|
|
initialState,
|
|
|
reducers: {
|
|
|
- // 添加自定义标记到列表
|
|
|
- addCustomMarkToList: (state, action: PayloadAction<string>) => {
|
|
|
- const text = action.payload.trim();
|
|
|
- if (text && !state.customMarks.includes(text)) {
|
|
|
- state.customMarks.unshift(text); // 添加到列表开头
|
|
|
- // 限制列表长度,防止性能问题
|
|
|
- if (state.customMarks.length > 50) {
|
|
|
- state.customMarks = state.customMarks.slice(0, 50);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
// 设置选中的标记
|
|
|
setSelectedMark: (state, action: PayloadAction<string>) => {
|
|
|
state.selectedMark = action.payload;
|
|
|
@@ -50,14 +96,6 @@ const markPanelSlice = createSlice({
|
|
|
state.inputText = '';
|
|
|
},
|
|
|
|
|
|
- // 删除自定义标记
|
|
|
- removeCustomMark: (state, action: PayloadAction<string>) => {
|
|
|
- state.customMarks = state.customMarks.filter(mark => mark !== action.payload);
|
|
|
- if (state.selectedMark === action.payload) {
|
|
|
- state.selectedMark = '';
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
// 清空所有自定义标记
|
|
|
clearAllCustomMarks: (state) => {
|
|
|
state.customMarks = [];
|
|
|
@@ -65,15 +103,64 @@ const markPanelSlice = createSlice({
|
|
|
state.inputText = '';
|
|
|
},
|
|
|
},
|
|
|
+ extraReducers: (builder) => {
|
|
|
+ builder
|
|
|
+ // fetchMarksAsync
|
|
|
+ .addCase(fetchMarksAsync.pending, (state) => {
|
|
|
+ state.loading = true;
|
|
|
+ state.error = null;
|
|
|
+ })
|
|
|
+ .addCase(fetchMarksAsync.fulfilled, (state, action) => {
|
|
|
+ state.loading = false;
|
|
|
+ state.customMarks = action.payload;
|
|
|
+ })
|
|
|
+ .addCase(fetchMarksAsync.rejected, (state, action) => {
|
|
|
+ state.loading = false;
|
|
|
+ state.error = action.payload as string;
|
|
|
+ message.error(state.error);
|
|
|
+ })
|
|
|
+ // addMarkAsync
|
|
|
+ .addCase(addMarkAsync.pending, (state) => {
|
|
|
+ state.loading = true;
|
|
|
+ state.error = null;
|
|
|
+ })
|
|
|
+ .addCase(addMarkAsync.fulfilled, (state, action) => {
|
|
|
+ state.loading = false;
|
|
|
+ // 刷新列表
|
|
|
+ // 这里可以选择重新获取列表或者手动添加
|
|
|
+ // 暂时手动添加,假设后端返回了完整的mark对象
|
|
|
+ // 实际情况可能需要重新调用 fetchMarksAsync
|
|
|
+ })
|
|
|
+ .addCase(addMarkAsync.rejected, (state, action) => {
|
|
|
+ state.loading = false;
|
|
|
+ state.error = action.payload as string;
|
|
|
+ message.error(state.error);
|
|
|
+ })
|
|
|
+ // deleteMarkAsync
|
|
|
+ .addCase(deleteMarkAsync.pending, (state) => {
|
|
|
+ state.loading = true;
|
|
|
+ state.error = null;
|
|
|
+ })
|
|
|
+ .addCase(deleteMarkAsync.fulfilled, (state, action) => {
|
|
|
+ state.loading = false;
|
|
|
+ state.customMarks = state.customMarks.filter(mark => mark.id !== action.payload);
|
|
|
+ if (state.selectedMark === state.customMarks.find(m => m.id === action.payload)?.mark) {
|
|
|
+ state.selectedMark = '';
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .addCase(deleteMarkAsync.rejected, (state, action) => {
|
|
|
+ state.loading = false;
|
|
|
+ state.error = action.payload as string;
|
|
|
+ message.error(state.error);
|
|
|
+ });
|
|
|
+ },
|
|
|
});
|
|
|
|
|
|
export const {
|
|
|
- addCustomMarkToList,
|
|
|
setSelectedMark,
|
|
|
setInputText,
|
|
|
setSelectedViewportIds,
|
|
|
clearInputText,
|
|
|
- removeCustomMark,
|
|
|
clearAllCustomMarks,
|
|
|
} = markPanelSlice.actions;
|
|
|
|