mirror of
https://github.com/Funkoala14/CreatorCenter_OOIN.git
synced 2025-06-07 22:58:14 +08:00
566 lines
19 KiB
JavaScript
566 lines
19 KiB
JavaScript
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;
|