/** * 滑动参数调节面板状态管理 */ import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from '../store'; import type { ProcessingStyle, LUTType, FullProcessingParams } from '../../types/imageProcessing'; import { PROCESSING_PRESETS, DEFAULT_ALGORITHM } from '../../domain/processingPresets'; import { DEFAULT_LUT } from '../../domain/lutConfig'; import { getImageProcessingParams, saveImageProcessingParams } from '../../API/imageActions'; /** * 状态接口 */ interface SliderAdjustmentPanelState { // 当前图像ID currentImageId: string | null; // 当前参数值 parameters: FullProcessingParams; // 当前选择的风格 selectedStyle: ProcessingStyle; // 当前选择的算法(独立管理) selectedAlgorithm: string; // 当前选择的LUT(独立管理) selectedLUT: LUTType; // 是否正在加载 isLoading: boolean; // 是否正在保存 isSaving: boolean; // 初始加载标志(区分初始加载和用户操作) isInitialLoad: boolean; // 错误信息 error: string | null; } /** * 初始状态 */ const initialState: SliderAdjustmentPanelState = { currentImageId: null, parameters: PROCESSING_PRESETS['均衡'].params, selectedStyle: '均衡', selectedAlgorithm: DEFAULT_ALGORITHM, selectedLUT: DEFAULT_LUT, isLoading: false, isSaving: false, isInitialLoad: true, error: null, }; /** * 异步操作:加载图像处理参数 */ export const loadImageProcessingParams = createAsyncThunk( 'sliderAdjustmentPanel/loadParams', async (sopInstanceUid: string, { rejectWithValue }) => { try { const response = await getImageProcessingParams(sopInstanceUid); return { sopInstanceUid, params: response.data, }; } catch (error) { return rejectWithValue( error instanceof Error ? error.message : '加载参数失败' ); } } ); /** * 异步操作:保存图像处理参数 */ export const saveProcessingParams = createAsyncThunk( 'sliderAdjustmentPanel/saveParams', async ( { sopInstanceUid, params }: { sopInstanceUid: string; params: { contrast: number; detail: number; latitude: number; noise: number } }, { rejectWithValue } ) => { try { await saveImageProcessingParams(sopInstanceUid, params); return params; } catch (error) { return rejectWithValue( error instanceof Error ? error.message : '保存参数失败' ); } } ); /** * Slice */ const sliderAdjustmentPanelSlice = createSlice({ name: 'sliderAdjustmentPanel', initialState, reducers: { /** * 设置当前图像ID */ setCurrentImageId: (state, action: PayloadAction) => { state.currentImageId = action.payload; state.isInitialLoad = true; }, /** * 更新单个参数 */ updateParameter: ( state, action: PayloadAction<{ name: keyof FullProcessingParams; value: number }> ) => { const { name, value } = action.payload; state.parameters[name] = value; // 用户手动调整参数后,不再是初始加载状态 state.isInitialLoad = false; }, /** * 应用风格预设 */ applyPreset: (state, action: PayloadAction) => { const preset = PROCESSING_PRESETS[action.payload]; state.selectedStyle = action.payload; state.parameters = { ...preset.params }; // 应用风格后,不再是初始加载状态 state.isInitialLoad = false; }, /** * 重置为当前风格的默认值 */ resetToPreset: (state) => { const preset = PROCESSING_PRESETS[state.selectedStyle]; state.parameters = { ...preset.params }; state.isInitialLoad = false; }, /** * 设置算法 */ setAlgorithm: (state, action: PayloadAction) => { state.selectedAlgorithm = action.payload; }, /** * 设置LUT */ setLUT: (state, action: PayloadAction) => { state.selectedLUT = action.payload; }, /** * 清除错误 */ clearError: (state) => { state.error = null; }, /** * 重置状态 */ resetState: () => initialState, }, extraReducers: (builder) => { builder // 加载参数 .addCase(loadImageProcessingParams.pending, (state) => { state.isLoading = true; state.error = null; }) .addCase(loadImageProcessingParams.fulfilled, (state, action) => { state.isLoading = false; state.currentImageId = action.payload.sopInstanceUid; // 只更新后端返回的4个参数,保持前端的 brightness 和 sharpness state.parameters.contrast = action.payload.params.contrast; state.parameters.detail = action.payload.params.detail; state.parameters.latitude = action.payload.params.latitude; state.parameters.noise = action.payload.params.noise; // 标记为初始加载,避免触发自动保存 state.isInitialLoad = true; }) .addCase(loadImageProcessingParams.rejected, (state, action) => { state.isLoading = false; state.error = action.payload as string; // 加载失败时使用默认值 state.parameters = PROCESSING_PRESETS[state.selectedStyle].params; }) // 保存参数 .addCase(saveProcessingParams.pending, (state) => { state.isSaving = true; state.error = null; }) .addCase(saveProcessingParams.fulfilled, (state) => { state.isSaving = false; }) .addCase(saveProcessingParams.rejected, (state, action) => { state.isSaving = false; state.error = action.payload as string; }); }, }); /** * Actions */ export const { setCurrentImageId, updateParameter, applyPreset, resetToPreset, setAlgorithm, setLUT, clearError, resetState, } = sliderAdjustmentPanelSlice.actions; /** * Selectors */ export const selectCurrentImageId = (state: RootState) => state.sliderAdjustmentPanel.currentImageId; export const selectParameters = (state: RootState) => state.sliderAdjustmentPanel.parameters; export const selectSelectedStyle = (state: RootState) => state.sliderAdjustmentPanel.selectedStyle; export const selectSelectedAlgorithm = (state: RootState) => state.sliderAdjustmentPanel.selectedAlgorithm; export const selectSelectedLUT = (state: RootState) => state.sliderAdjustmentPanel.selectedLUT; export const selectIsLoading = (state: RootState) => state.sliderAdjustmentPanel.isLoading; export const selectIsSaving = (state: RootState) => state.sliderAdjustmentPanel.isSaving; export const selectIsInitialLoad = (state: RootState) => state.sliderAdjustmentPanel.isInitialLoad; export const selectError = (state: RootState) => state.sliderAdjustmentPanel.error; /** * Reducer */ export default sliderAdjustmentPanelSlice.reducer;