Files
mini-programs/src/store/listStore.ts
2025-11-22 22:50:18 +08:00

678 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { create } from "zustand";
import dayjs from "dayjs";
import {
getGamesList,
getGamesIntegrateList,
getSearchHistory,
clearHistory,
searchSuggestion,
getGamesCount,
getCities,
getCityQrCode,
getDistricts,
} from "../services/listApi";
import {
ListActions,
IFilterOptions,
ListState,
IPayload,
} from "../../types/list/types";
function translateCityData(dataTree) {
return dataTree.map((item) => {
const { children, ...rest } = item;
// 只保留两级:国家和省份,去掉第三级(区域)
const processedChildren = children?.length > 0
? children.map(child => ({
...child,
text: child.name,
label: child.name,
value: child.name,
children: null, // 去掉第三级
}))
: null;
return {
...rest,
text: rest.name,
label: rest.name,
value: rest.name,
children: processedChildren,
};
});
}
// 完整的 Store 类型
type TennisStore = ListState & ListActions;
const defaultDateRange: [string, string] = [dayjs().format('YYYY-MM-DD'), dayjs().add(1, 'M').format('YYYY-MM-DD')]
const defaultFilterOptions: IFilterOptions = {
dateRange: defaultDateRange, // 日期区间
timeSlot: "", // 时间段
ntrp: [1, 5], // NTRP 水平区间
venueType: "", // 场地类型
playType: "", // 玩法
};
// const defaultDistance = "all"; // 默认距离
const defaultDistanceQuickFilter = {
distanceFilter: "",
order: "0",
district: "", // 新增:行政区筛选
};
const defaultPageOption = {
page: 1,
pageSize: 20,
};
// 页面状态默认值
const pageStateDefaultValue = {
// 列表数据
data: [],
// 推荐列表
recommendList: [],
// 是否展示综合筛选弹窗
isShowFilterPopup: false,
// 综合筛选项
filterOptions: defaultFilterOptions,
// 距离筛选和快捷筛选
distanceQuickFilter: defaultDistanceQuickFilter,
// 综合筛选 选择的筛选数量
filterCount: 0,
// 分页
pageOption: defaultPageOption,
// 球局数量
gamesNum: 0,
// 是否还有更多数据
isHasMoreData: true,
// 是否展示无数据
isShowNoData: false,
}
// 列表页状态
const listPageStateDefaultValue = {
...pageStateDefaultValue,
// 列表页是否显示搜索框自定义导航
isShowInputCustomerNavBar: false,
}
// 搜索页状态
const searchPageStateDefaultValue = {
...pageStateDefaultValue,
// 搜索结果数据
data: [],
// 联想词
suggestionList: [],
// 是否显示联想词
isShowSuggestion: false,
// 搜索历史数据
searchHistory: [],
// 搜索历史数据默认 Top 15
searchHistoryParams: {
page: 1,
pageSize: 15,
},
}
// const now = new Date();
// 公共属性
const commonStateDefaultValue = {
// 是否是搜索结果页
isSearchResult: false,
// 是否加载中
loading: false,
// 错误信息
error: null,
// 位置
location: {
latitude: 0,
longitude: 0,
},
// 搜索的value
searchValue: "",
// 日期区间
// dateRange: [now, new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000)],
// 距离筛选数据
distanceData: [
{ id: 0, label: "全城", value: "" },
{ id: 1, label: "3km", value: "3" },
{ id: 2, label: "5km", value: "5" },
{ id: 3, label: "10km", value: "10" },
],
// 快捷筛选数据
quickFilterData: [
{ label: "智能排序", value: "0" },
{ label: "距离更近", value: "distance" },
{ label: "时间更近", value: "time" },
],
// 气泡日期范围
dateRangeOptions: [
{ id: 1, label: "本周末", value: "1" },
{ id: 2, label: "一周内", value: "2" },
{ id: 3, label: "一月内", value: "3" },
],
// 时间气泡数据
timeBubbleData: [
{ id: 1, label: "晨间 6:00-10:00", value: "6:00-10:00" },
{ id: 2, label: "上午 10:00-12:00", value: "10:00-12:00" },
{ id: 3, label: "中午 12:00-14:00", value: "12:00-14:00" },
{ id: 4, label: "下午 14:00-18:00", value: "14:00-18:00" },
{ id: 5, label: "晚上 18:00-22:00", value: "18:00-22:00" },
{ id: 6, label: "夜间 22:00-24:00", value: "22:00-24:00" },
],
cities: [],
cityQrCode: [],
area: ['', ''] as [string, string], // 改为两级:国家、省份
districts: [], // 新增:行政区列表
}
// 创建 store
export const useListStore = create<TennisStore>()((set, get) => ({
currentPage: "",
// 列表页
listPageState: listPageStateDefaultValue,
// 搜索及搜索结果页
searchPageState: searchPageStateDefaultValue,
...commonStateDefaultValue,
gamesNum: 0,
// 组装搜索数据
getSearchParams: () => {
const state = get();
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const filterOptions = currentPageState?.filterOptions || {};
// 全城和快捷筛选
const distanceQuickFilter = currentPageState?.distanceQuickFilter || {};
const { distanceFilter, order, district } = distanceQuickFilter || {};
// 从 area 中获取省份名称area 格式: ["中国", 省份]
const province = state.area?.[1] || ""; // area[1] 是省份
// city 参数逻辑:
// 1. 如果选择了行政区district 有值使用行政区的名称label
// 2. 如果是"全城"distanceFilter 为空),不传 city
let city: string | undefined = undefined;
if (district) {
// 从 districts 数组中查找对应的行政区名称
const selectedDistrict = state.districts.find(item => item.value === district);
if (selectedDistrict) {
city = selectedDistrict.label; // 传递行政区名称,如"静安"
}
}
// 如果是"全城"distanceFilter 为空city 保持 undefined不会被传递
// 使用 filterOptions 中的 dateRange
const dateRange: [string, string] = filterOptions?.dateRange || defaultDateRange;
const searchOption: any = {
...filterOptions,
title: state.searchValue,
ntrpMin: filterOptions?.ntrp?.[0],
ntrpMax: filterOptions?.ntrp?.[1],
dateRange: dateRange, // 确保始终是两个值的数组
distanceFilter: distanceFilter,
province: province, // 添加省份参数
};
// 只在有值时添加 city 参数
if (city) {
searchOption.city = city;
}
const params = {
pageOption: currentPageState?.pageOption,
seachOption: searchOption,
order: order,
lat: state?.location?.latitude,
lng: state?.location?.longitude,
};
return params;
},
// 设置列表结果
setListData: (payload: IPayload & { isAppend?: boolean }) => {
const state = get();
const { error, data, loading, count, isAppend = false } = payload;
const isHasMoreData = count > 0;
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const currentData = currentPageState?.data || [];
const newData = isAppend ? [...currentData, ...(data || [])] : (data || []);
state.updateCurrentPageState({
data: newData,
isHasMoreData,
isShowNoData: newData?.length === 0,
});
set({
error,
loading,
});
},
// 获取列表数据(常规搜索)
fetchMatches: async (params, isFirstLoad = false, isAppend = false) => {
if (get().loading) {
return;
}
set({ loading: true, error: null });
const { getSearchParams, setListData } = get();
try {
const searchParams = getSearchParams() || {};
const reqParams = {
...(searchParams || {}),
...params,
};
// 获取当前页面的距离筛选
const state = get();
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const distanceQuickFilter = currentPageState?.distanceQuickFilter || {};
// 是否选择了智能排序
const isIntegrate = distanceQuickFilter?.order === "0";
let fetchFn = getGamesList;
if (isIntegrate) {
reqParams.order = "";
fetchFn = getGamesIntegrateList;
// 第一次进入页面时传入 isRefresh 参数
if (isFirstLoad) {
reqParams.seachOption.isRefresh = true;
}
}
const resData = (await fetchFn(reqParams)) || {};
const { data = {}, code } = resData;
if (code !== 0) {
setListData({
error: "-1",
data: [],
loading: false,
count: 0,
isAppend,
});
return Promise.reject(new Error('获取数据失败'));
}
const { count, rows } = data;
setListData({
error: '',
data: rows || [],
loading: false,
count,
isAppend,
});
return Promise.resolve();
} catch (error) {
setListData({
error: "-1",
data: [],
loading: false,
count: 0,
isAppend,
});
return Promise.reject(error);
}
},
// 获取列表数据
getMatchesData: async () => {
const { fetchMatches } = get();
return await fetchMatches({}, true); // 第一次进入页面,传入 isFirstLoad = true
},
// 同时更新两个列表接口(常规列表和智能排序列表)
refreshBothLists: async () => {
const state = get();
const { getSearchParams, setListData } = state;
const { getGamesList, getGamesIntegrateList } = await import("../services/listApi");
try {
const searchParams = getSearchParams() || {};
// 调用常规列表接口
const listParams = {
...searchParams,
order: searchParams.order || "distance",
};
const listRes = await getGamesList(listParams);
// 调用智能排序列表接口
const integrateParams = {
...searchParams,
order: "",
seachOption: {
...searchParams.seachOption,
isRefresh: true,
},
};
const integrateRes = await getGamesIntegrateList(integrateParams);
// 根据当前排序方式更新对应的数据
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const distanceQuickFilter = currentPageState?.distanceQuickFilter || {};
const isIntegrate = distanceQuickFilter?.order === "0";
if (listRes?.code === 0 && listRes?.data) {
const { count, rows } = listRes.data;
if (!isIntegrate) {
// 如果当前是常规排序,更新常规列表数据
setListData({
error: '',
data: rows || [],
loading: false,
count,
isAppend: false,
});
}
}
if (integrateRes?.code === 0 && integrateRes?.data) {
const { count, rows, recommendList } = integrateRes.data;
if (isIntegrate) {
// 如果当前是智能排序,更新智能排序列表数据
setListData({
error: '',
data: rows || [],
loading: false,
count,
isAppend: false,
});
}
// 无论当前排序方式如何,都更新推荐列表
state.updateCurrentPageState({
recommendList: recommendList || [],
});
}
return Promise.resolve();
} catch (error) {
console.error("更新列表数据失败:", error);
return Promise.reject(error);
}
},
// 获取球局数量
fetchGetGamesCount: async () => {
const { getSearchParams } = get();
const params = getSearchParams() || {};
const resData = (await getGamesCount(params)) || {};
const gamesNum = resData?.data?.count || 0;
set({ gamesNum });
},
// 获取历史搜索数据
getSearchHistory: async () => {
try {
const state = get();
const params = state.searchPageState?.searchHistoryParams || {};
const resData = (await getSearchHistory(params)) || {};
const searchHistory = resData?.data?.records || [];
set({
searchPageState: {
...state.searchPageState,
searchHistory,
},
});
} catch (error) { }
},
// 清空历史记录
clearHistory: async () => {
try {
const state = get();
const params = {};
const resData = (await clearHistory(params)) || {};
if (resData?.code === 0) {
set({
searchPageState: {
...state.searchPageState,
searchHistory: [],
},
});
}
} catch (error) { }
},
// 获取联想
searchSuggestion: async (val: string) => {
try {
const state = get();
const resData = (await searchSuggestion({ keyword: val, limit: 10 })) || {};
const recommendations = resData?.data?.recommendations || [];
const total = resData?.data?.total;
set({
searchPageState: {
...state.searchPageState,
suggestionList: recommendations,
isShowSuggestion: total > 0,
},
});
} catch (error) {
const state = get();
set({
searchPageState: {
...state.searchPageState,
suggestionList: [],
isShowSuggestion: true,
},
});
}
},
// 清除错误信息
clearError: () => {
set({ error: null });
},
getCurrentPageState: () => {
const state = get();
return {
currentPageState: state.isSearchResult ? state.searchPageState : state.listPageState,
currentPageKey: state.isSearchResult ? "searchPageState" : "listPageState",
};
},
// 更新当前页面状态
updateCurrentPageState: (payload: Record<string, any>) => {
const state = get();
const { currentPageState, currentPageKey } = state.getCurrentPageState();
set({
[currentPageKey]: { ...currentPageState, ...payload }
});
},
// 更新综合筛选项
updateFilterOptions: (payload: Record<string, any>) => {
const state = get();
const { currentPageState } = state.getCurrentPageState();
const filterOptions = { ...currentPageState?.filterOptions, ...payload };
const filterCount = Object.values(filterOptions).filter(Boolean).length;
state.updateCurrentPageState({
filterOptions,
filterCount,
pageOption: defaultPageOption,
});
// 获取球局数量
state.fetchGetGamesCount();
},
// 更新距离和快捷筛选
updateDistanceQuickFilter: (payload: Record<string, any>) => {
const state = get();
const { currentPageState } = state.getCurrentPageState();
const { distanceQuickFilter } = currentPageState || {};
const newDistanceQuickFilter = { ...distanceQuickFilter, ...payload };
state.updateCurrentPageState({
distanceQuickFilter: newDistanceQuickFilter,
pageOption: defaultPageOption,
});
state.getMatchesData();
state.fetchGetGamesCount();
},
// 清空综合筛选选项
clearFilterOptions: () => {
const state = get();
const { getMatchesData, fetchGetGamesCount } = state;
state.updateCurrentPageState({
filterOptions: defaultFilterOptions,
filterCount: 0,
pageOption: defaultPageOption,
});
getMatchesData();
fetchGetGamesCount();
},
// 加载更多数据
loadMoreMatches: async () => {
const state = get();
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const { pageOption, isHasMoreData } = currentPageState || {};
if (!isHasMoreData) {
return Promise.resolve();
}
const newPageOption = {
page: (pageOption?.page || 1) + 1,
pageSize: 20,
};
state.updateCurrentPageState({
pageOption: newPageOption,
});
// 加载更多时追加数据到现有数组
return await state.fetchMatches({}, false, true);
},
// 初始化搜索条件 重新搜索
initialFilterSearch: async (isSearchData = false) => {
const state = get();
const { getMatchesData, fetchGetGamesCount } = state;
if (state.isSearchResult) {
set({
searchPageState: {
...searchPageStateDefaultValue
},
// loading: true,
});
} else {
set({
listPageState: {
...listPageStateDefaultValue
},
// loading: true,
});
}
if (!isSearchData) {
return;
}
await fetchGetGamesCount();
await getMatchesData();
},
// 更新store数据
updateState: (payload: Record<string, any>) => {
set({
...(payload || {}),
});
},
// 更新列表页状态中的特定字段
updateListPageState: (payload: Record<string, any>) => {
console.log("===更新列表页状态:", payload);
const state = get();
set({
listPageState: {
...state.listPageState,
...payload,
},
});
},
// 更新搜索页状态中的特定字段
updateSearchPageState: (payload: Record<string, any>) => {
const state = get();
set({
searchPageState: {
...state.searchPageState,
...payload,
},
});
console.log("===更新搜索页状态:", state);
},
async getCities() {
const res = await getCities();
const state = get();
set({
...state,
cities: translateCityData(res.data),
})
},
async getCityQrCode() {
const res = await getCityQrCode();
const state = get();
set({
...state,
cityQrCode: res.data,
})
},
// 新增:获取行政区列表
async getDistricts() {
try {
const state = get();
// 从 area 中获取省份area 格式: ["中国", 省份, 城市]
const country = "中国";
const province = state.area?.at(1) || "上海"; // area[1] 是省份
const res = await getDistricts({
country,
state: province
});
if (res.code === 0 && res.data) {
const districts = res.data.map((item) => ({
label: item.cn_city,
value: item.id.toString(),
id: item.id,
}));
set({ districts });
return districts;
}
return [];
} catch (error) {
console.error("获取行政区列表失败:", error);
return [];
}
},
updateArea(payload: [string, string]) {
const state = get();
set({
...state,
area: payload,
})
},
}));
// 导出便捷的 hooks
export const useListState = () => useListStore((state) => state);