import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import api from '@/services/api'; import { setNotificationBarMessage } from './notificationBarSlice'; const mockVideos = [ { id: 1, title: 'Collagen + Biotin = your beauty routine’s new besties. For hair, skin, nails, and join...', picture: 'https://api.dicebear.com/7.x/micah/svg?seed=1', releaseTime: '2025-01-01', views: 1000, likes: 100, }, { id: 2, title: 'Collagen + Biotin = your beauty routine’s new besties. For hair, skin, nails, and join...', picture: 'https://api.dicebear.com/7.x/micah/svg?seed=2', releaseTime: '2025-01-01', views: 1000, likes: 100, }, { id: 3, title: 'Collagen + Biotin = your beauty routine’s new besties. For hair, skin, nails, and join...', picture: 'https://api.dicebear.com/7.x/micah/svg?seed=3', releaseTime: '2025-01-01', views: 1000, likes: 100, }, ]; // 模拟创作者数据,实际项目中会从API获取 export const mockCreators = [ { id: 1, name: 'name', avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=1', category: 'Phones & Electronics', e_commerce_level: 'L2', exposure_level: 'KOC-1', followers: '162.2k', gmv: '$534.1k', soldPercentage: '18.1%', avg_video_views: '1.9k', hasEcommerce: true, hasTiktok: true, verified: true, videos: mockVideos, videosWithProduct: mockVideos, }, { id: 2, name: 'name', avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=2', category: 'Womenswear & Underwear', e_commerce_level: 'L3', exposure_level: 'KOL-3', followers: '162.2k', gmv: '$534.1k', soldPercentage: '18.1%', avg_video_views: '1.9k', hasEcommerce: false, hasTiktok: true, verified: false, videos: mockVideos, videosWithProduct: mockVideos, }, { id: 3, name: 'name', avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=3', category: 'Sports & Outdoor', e_commerce_level: 'L4', exposure_level: 'KOC-2', followers: '162.2k', gmv: '$534.1k', soldPercentage: '18.1%', avg_video_views: '1.9k', hasEcommerce: true, hasTiktok: true, verified: false, videos: mockVideos, videosWithProduct: mockVideos, }, { id: 4, name: 'name', avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=4', category: 'Food & Beverage', e_commerce_level: 'L1', exposure_level: 'KOC-2', followers: '162.2k', gmv: '$534.1k', soldPercentage: '18.1%', avg_video_views: '1.9k', hasEcommerce: true, hasTiktok: true, hasInstagram: true, hasYoutube: true, verified: true, videos: mockVideos, videosWithProduct: mockVideos, }, { id: 5, name: 'name', avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=5', category: 'Health', e_commerce_level: 'L5', exposure_level: 'KOL-2', followers: '162.2k', gmv: '$534.1k', soldPercentage: '18.1%', avg_video_views: '1.9k', hasEcommerce: false, hasTiktok: true, hasInstagram: true, hasYoutube: true, verified: true, videos: mockVideos, videosWithProduct: mockVideos, }, { id: 6, name: 'name', avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=6', category: 'Kitchenware', e_commerce_level: 'New tag', exposure_level: 'New tag', followers: '162.2k', gmv: '$534.1k', soldPercentage: '18.1%', avg_video_views: '1.9k', hasEcommerce: true, hasTiktok: true, hasInstagram: true, hasYoutube: true, verified: false, videos: mockVideos, videosWithProduct: mockVideos, }, ]; // 模拟API获取数据的异步Thunk // export const fetchCreators = createAsyncThunk('creators/fetchCreators', async ({ path }, { getState }) => { // // 模拟API调用延迟 // await new Promise((resolve) => setTimeout(resolve, 500)); // // 获取当前的筛选条件 // const state = getState(); // const filters = state.filters; // // 应用筛选逻辑(实际项目中可能在服务器端进行) // let filteredCreators = [...mockCreators]; // // 如果有选定的类别,进行筛选 // if (filters.category.length > 0) { // filteredCreators = filteredCreators.filter((creator) => filters.category.includes(creator.category)); // } // // 如果有选定的电商评级,进行筛选 // if (filters.ecommerceRatings.length > 0) { // filteredCreators = filteredCreators.filter((creator) => // filters.ecommerceRatings.includes(creator.e_commerce_level) // ); // } // // 如果有选定的曝光评级,进行筛选 // if (filters.exposureRatings.length > 0) { // filteredCreators = filteredCreators.filter((creator) => // filters.exposureRatings.includes(creator.exposure_level) // ); // } // // 筛选观看量范围 // if (filters.viewsRange.length === 2) { // const minViews = filters.viewsRange[0]; // const maxViews = filters.viewsRange[1]; // filteredCreators = filteredCreators.filter((creator) => { // // 将带k的字符串转换为数字 // const viewsStr = creator.avg_video_views; // let views = parseFloat(viewsStr); // if (viewsStr.includes('k')) { // views *= 1000; // } else if (viewsStr.includes('M')) { // views *= 1000000; // } // return views >= minViews && views <= maxViews; // }); // } // return filteredCreators; // }); const initialState = { publicCreators: [], privateCreators: [], publicTiktokCreators: [], publicInstagramCreators: [], publicYoutubeCreators: [], privateTiktokCreators: [], privateInstagramCreators: [], privateYoutubeCreators: [], status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed' error: null, selectedCreators: [], selectedCreator: null, pagination: { current_page: 1, total_pages: 0, total_count: 0, has_next: false, has_prev: false, }, hasMore: true, isLoadingMore: false, }; export const fetchCreators = createAsyncThunk( 'creators/fetchCreators', async ({ page = 1 }, { getState, rejectWithValue }) => { try { const state = getState(); const {pricing, ...filter} = state.filters; const { code, data, message, pagination } = await api.post( `/daren_detail/public/creators/filter/?page=${page}`, { filter } ); if (code === 200) { return { data, pagination }; } else { throw new Error(message); } } catch (error) { return rejectWithValue(error.message); } } ); export const fetchPrivateCreators = createAsyncThunk( 'creators/fetchPrivateCreators', async ({ page = 1 }, { getState, rejectWithValue, dispatch }) => { try { const state = getState(); const filter = state.filters; const { code, data, message, pagination } = await api.post( `/daren_detail/private/pools/creators/filter/?page=${page}`, { pool_id: 1, filter } ); if (code === 200) { return { data, pagination }; } else { throw new Error(message); } } catch (error) { return rejectWithValue(error.message); } } ); /** * 搜索公有达人库达人 * @param {Object} params * @param {string} params.q 搜索关键词 * @param {string} params.mode 搜索类型 OR 和 AND 固定or * @param {number} params.page 页码 * @param {number} params.page_size 每页条数 */ export const searchCreators = createAsyncThunk( 'creators/searchPublicCreators', async (params, { rejectWithValue, dispatch }) => { try { const response = await api.get(`/daren_detail/creators/search/`, { params }); if (response.code === 200) { return response; } else { throw new Error(response.message); } } catch (error) { return rejectWithValue(error.message); } } ); /** * 搜索私有达人库达人 * @param {Object} params * @param {string} params.q 搜索关键词 * @param {string} params.mode 搜索类型 OR 和 AND 固定or * @param {number} params.page 页码 * @param {number} params.page_size 每页条数 */ export const searchPrivateCreators = createAsyncThunk( 'creators/searchPrivateCreators', async (params, { rejectWithValue, dispatch }) => { try { const response = await api.get(`/daren_detail/private/creators/search/`, { params }); if (response.code === 200) { return response; } else { throw new Error(response.message); } } catch (error) { return rejectWithValue(error.message); } } ); export const fetchCreatorDetail = createAsyncThunk( 'creators/fetchCreatorDetail', async ({ creatorId }, { dispatch, rejectWithValue }) => { try { const response = await api.get(`/daren_detail/creators/${creatorId}`); if (response.code === 200) { return response; } else { throw new Error(response.message); } } catch (error) { console.log('fetchCreatorDetail.rejected', error); return rejectWithValue(error.message); } } ); export const fetchCreatorMetrics = createAsyncThunk( 'creators/fetchCreatorMetrics', async ({ creatorId }, { rejectWithValue }) => { try { const response = await api.get(`/daren_detail/creators/${creatorId}/metrics`); if (response.code === 200 || response.code === 201) { return response; } else { throw new Error(response.message); } } catch (error) { return rejectWithValue(error.message); } } ); export const fetchCreatorFollowers = createAsyncThunk( 'creators/fetchCreatorFollowers', async ({ creatorId }, { rejectWithValue }) => { try { const response = await api.get(`/daren_detail/creator/${creatorId}/followers`); if (response.code === 200) { return response; } else { throw new Error(response.message); } } catch (error) { return rejectWithValue(error.message); } } ); export const fetchCreatorTrends = createAsyncThunk( 'creators/fetchCreatorTrends', async ({ creatorId }, { rejectWithValue }) => { try { const response = await api.get(`/daren_detail/creator/${creatorId}/trends`); if (response.code === 200) { return response; } else { throw new Error(response.message); } } catch (error) { return rejectWithValue(error.message); } } ); export const fetchCreatorVideos = createAsyncThunk( 'creators/fetchCreatorVideos', async ({ creatorId }, { rejectWithValue }) => { try { const response = await api.get(`/daren_detail/creator/${creatorId}/videos`); if (response.code === 200) { return response; } else { throw new Error(response.message); } } catch (error) { return rejectWithValue(error.message); } } ); export const addCreatorsToCampaign = createAsyncThunk( 'creators/addCreatorsToCampaign', async ({ creatorIds, campaignId }, { getState, rejectWithValue }) => { try { const response = await api.post(`/daren_detail/campaigns/add/`, { creator_ids: creatorIds, campaign_id: campaignId, }); if (response.code === 200) { dispatch( setNotificationBarMessage({ message: 'Creators added to campaign successfully', type: 'success' }) ); return response; } else { dispatch(setNotificationBarMessage({ message: response.message, type: 'error' })); throw new Error(response.message); } } catch (error) { return rejectWithValue(error.message); } } ); const creatorsSlice = createSlice({ name: 'creators', initialState, reducers: { toggleCreatorSelection: (state, action) => { const creatorId = action.payload; const isSelected = state.selectedCreators.includes(creatorId); if (isSelected) { state.selectedCreators = state.selectedCreators.filter((id) => id.toString() !== creatorId.toString()); } else { state.selectedCreators.push(creatorId); } }, selectAllCreators: (state, action) => { if (action.payload === 'database') { state.selectedCreators = state.publicCreators.map((creator) => creator.creator_id); } else { state.selectedCreators = state.privateCreators.map((creator) => creator.creator_id); } }, clearCreatorSelection: (state) => { state.selectedCreators = []; }, clearCreator: (state) => { state.selectedCreator = null; }, resetCreators: (state) => { state.publicCreators = []; state.privateCreators = []; state.pagination = initialState.pagination; state.hasMore = true; state.isLoadingMore = false; }, }, extraReducers: (builder) => { builder .addCase(fetchCreators.pending, (state) => { state.status = 'loading'; if (state.publicCreators.length === 0) { } else { state.isLoadingMore = true; } }) .addCase(fetchCreators.fulfilled, (state, action) => { state.status = 'succeeded'; state.isLoadingMore = false; const { data, pagination } = action.payload; if (pagination.current_page === 1) { state.publicCreators = data; } else { state.publicCreators = [...state.publicCreators, ...data]; } state.pagination = pagination; state.hasMore = pagination.has_next; }) .addCase(fetchCreators.rejected, (state, action) => { state.status = 'failed'; state.isLoadingMore = false; state.error = action.payload; }) .addCase(fetchPrivateCreators.pending, (state) => { if (state.privateCreators?.length === 0) { state.status = 'loading'; } else { state.isLoadingMore = true; } }) .addCase(fetchPrivateCreators.fulfilled, (state, action) => { state.status = 'succeeded'; state.isLoadingMore = false; const { data, pagination } = action.payload; if (pagination.current_page === 1) { state.privateCreators = data; } else { state.privateCreators = [...state.privateCreators, ...data]; } state.pagination = pagination; state.hasMore = pagination.has_next; }) .addCase(fetchPrivateCreators.rejected, (state, action) => { console.log('fetchPrivateCreators.rejected', action); state.status = 'failed'; state.isLoadingMore = false; state.error = action.payload; }) .addCase(fetchCreatorDetail.pending, (state) => { state.status = 'loading'; }) .addCase(fetchCreatorDetail.fulfilled, (state, action) => { state.status = 'succeeded'; const { data } = action.payload; state.selectedCreator = data; }) .addCase(fetchCreatorDetail.rejected, (state, action) => { state.status = 'failed'; state.error = action.error.message; }) .addCase(fetchCreatorMetrics.fulfilled, (state, action) => { state.selectedCreator.metricsData = action.payload.data; }) .addCase(fetchCreatorFollowers.fulfilled, (state, action) => { state.selectedCreator.followerData = action.payload.data; }) .addCase(fetchCreatorTrends.fulfilled, (state, action) => { state.selectedCreator.trendsData = action.payload.data; }) .addCase(fetchCreatorVideos.fulfilled, (state, action) => { state.selectedCreator.videosData = action.payload.data; }) .addCase(searchCreators.pending, (state) => { state.status = 'loading'; }) .addCase(searchPrivateCreators.pending, (state) => { state.status = 'loading'; }) .addCase(searchCreators.fulfilled, (state, action) => { state.publicCreators = action.payload.data; state.status = 'succeeded'; state.error = null; }) .addCase(searchPrivateCreators.fulfilled, (state, action) => { state.privateCreators = action.payload.data; state.status = 'succeeded'; state.error = null; }) .addCase(searchCreators.rejected, (state, action) => { state.status = 'failed'; state.error = action.payload; console.log('searchCreators.rejected', action); }) .addCase(searchPrivateCreators.rejected, (state, action) => { state.status = 'failed'; state.error = action.payload; }); }, }); export const { toggleCreatorSelection, selectAllCreators, clearCreatorSelection, clearCreator, setCreators, resetCreators, } = creatorsSlice.actions; export default creatorsSlice.reducer;