2025-05-09 10:18:49 +08:00
|
|
|
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
|
|
|
|
|
|
|
|
|
// 模拟创作者数据,实际项目中会从API获取
|
|
|
|
|
const mockCreators = [
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=1',
|
|
|
|
|
category: 'Phones & Electronics',
|
|
|
|
|
ecommerceLevel: 'L2',
|
|
|
|
|
exposureLevel: 'KOC-1',
|
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
|
|
|
|
avgViews: '1.9k',
|
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
verified: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 2,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=2',
|
|
|
|
|
category: 'Womenswear & Underwear',
|
|
|
|
|
ecommerceLevel: 'L3',
|
|
|
|
|
exposureLevel: 'KOL-3',
|
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
|
|
|
|
avgViews: '1.9k',
|
|
|
|
|
hasEcommerce: false,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
verified: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 3,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=3',
|
|
|
|
|
category: 'Sports & Outdoor',
|
|
|
|
|
ecommerceLevel: 'L4',
|
|
|
|
|
exposureLevel: 'KOC-2',
|
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
|
|
|
|
avgViews: '1.9k',
|
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
verified: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 4,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=4',
|
|
|
|
|
category: 'Food & Beverage',
|
|
|
|
|
ecommerceLevel: 'L1',
|
|
|
|
|
exposureLevel: 'KOC-2',
|
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
|
|
|
|
avgViews: '1.9k',
|
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
hasInstagram: true,
|
|
|
|
|
hasYoutube: true,
|
|
|
|
|
verified: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 5,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=5',
|
|
|
|
|
category: 'Health',
|
|
|
|
|
ecommerceLevel: 'L5',
|
|
|
|
|
exposureLevel: 'KOL-2',
|
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
|
|
|
|
avgViews: '1.9k',
|
|
|
|
|
hasEcommerce: false,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
hasInstagram: true,
|
|
|
|
|
hasYoutube: true,
|
|
|
|
|
verified: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 6,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=6',
|
|
|
|
|
category: 'Kitchenware',
|
|
|
|
|
ecommerceLevel: 'New tag',
|
|
|
|
|
exposureLevel: 'New tag',
|
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
|
|
|
|
avgViews: '1.9k',
|
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
hasInstagram: true,
|
|
|
|
|
hasYoutube: true,
|
|
|
|
|
verified: false,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 模拟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.ecommerceLevel)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果有选定的曝光评级,进行筛选
|
|
|
|
|
if (filters.exposureRatings.length > 0) {
|
|
|
|
|
filteredCreators = filteredCreators.filter((creator) =>
|
|
|
|
|
filters.exposureRatings.includes(creator.exposureLevel)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 筛选观看量范围
|
|
|
|
|
if (filters.viewsRange.length === 2) {
|
|
|
|
|
const minViews = filters.viewsRange[0];
|
|
|
|
|
const maxViews = filters.viewsRange[1];
|
|
|
|
|
|
|
|
|
|
filteredCreators = filteredCreators.filter((creator) => {
|
|
|
|
|
// 将带k的字符串转换为数字
|
|
|
|
|
const viewsStr = creator.avgViews;
|
|
|
|
|
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 = {
|
|
|
|
|
creators: [],
|
|
|
|
|
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
|
|
|
|
|
error: null,
|
|
|
|
|
selectedCreators: [],
|
2025-05-16 09:22:08 +08:00
|
|
|
|
selectedCreator: null,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const creatorsSlice = createSlice({
|
|
|
|
|
name: 'creators',
|
|
|
|
|
initialState,
|
|
|
|
|
reducers: {
|
|
|
|
|
toggleCreatorSelection: (state, action) => {
|
|
|
|
|
const creatorId = action.payload;
|
|
|
|
|
const isSelected = state.selectedCreators.includes(creatorId);
|
|
|
|
|
|
|
|
|
|
if (isSelected) {
|
2025-05-16 09:22:08 +08:00
|
|
|
|
state.selectedCreators = state.selectedCreators.filter((id) => id.toString() !== creatorId.toString());
|
2025-05-09 10:18:49 +08:00
|
|
|
|
} else {
|
|
|
|
|
state.selectedCreators.push(creatorId);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
selectAllCreators: (state) => {
|
|
|
|
|
state.selectedCreators = state.creators.map((creator) => creator.id);
|
|
|
|
|
},
|
|
|
|
|
clearCreatorSelection: (state) => {
|
|
|
|
|
state.selectedCreators = [];
|
|
|
|
|
},
|
2025-05-16 09:22:08 +08:00
|
|
|
|
selectCreator: (state, action) => {
|
|
|
|
|
const id = action.payload;
|
|
|
|
|
state.selectedCreator = state.creators.find((creator) => creator.id.toString() === id.toString());
|
|
|
|
|
},
|
|
|
|
|
clearCreator: (state) => {
|
|
|
|
|
state.selectedCreator = null;
|
|
|
|
|
},
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
extraReducers: (builder) => {
|
|
|
|
|
builder
|
|
|
|
|
.addCase(fetchCreators.pending, (state) => {
|
|
|
|
|
state.status = 'loading';
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreators.fulfilled, (state, action) => {
|
|
|
|
|
state.status = 'succeeded';
|
|
|
|
|
state.creators = action.payload;
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreators.rejected, (state, action) => {
|
|
|
|
|
console.log(action);
|
|
|
|
|
state.status = 'failed';
|
|
|
|
|
state.error = action.error.message;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-05-16 09:22:08 +08:00
|
|
|
|
export const { toggleCreatorSelection, selectAllCreators, clearCreatorSelection, selectCreator, clearCreator } =
|
|
|
|
|
creatorsSlice.actions;
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
|
|
|
|
export default creatorsSlice.reducer;
|