Merge branch 'feature/juguohong/20250816'
This commit is contained in:
@@ -9,12 +9,12 @@ interface IProps {
|
||||
const CustomNavbar = (props: IProps) => {
|
||||
const { children } = props;
|
||||
const { statusNavbarHeightInfo } = useGlobalState();
|
||||
const { totalHeight } = statusNavbarHeightInfo;
|
||||
const { statusBarHeight, navbarHeight } = statusNavbarHeightInfo;
|
||||
|
||||
return (
|
||||
<View
|
||||
className={styles.customerNavbar}
|
||||
style={{ height: `${totalHeight}px` }}
|
||||
style={{ height: `${navbarHeight}px`, paddingTop: `${statusBarHeight}px`, }}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.distanceQuickFilterWrap {
|
||||
width: 100%;
|
||||
--nutui-menu-bar-line-height: 30px;
|
||||
|
||||
.nut-menu-bar {
|
||||
background-color: unset;
|
||||
@@ -35,19 +36,6 @@
|
||||
border-bottom-right-radius: 30px;
|
||||
}
|
||||
|
||||
// .nut-menu-container-item {
|
||||
// color: rgba(60, 60, 67, 0.6);
|
||||
// font-size: 14px;
|
||||
// font-weight: 600;
|
||||
// line-height: 20px;
|
||||
// }
|
||||
|
||||
// .nut-menu-container-item.active {
|
||||
// flex-direction: row-reverse;
|
||||
// justify-content: space-between;
|
||||
// color: #000;
|
||||
// }
|
||||
|
||||
.positionWrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Menu, Button } from "@nutui/nutui-react-taro";
|
||||
import { Image, View, Text } from "@tarojs/components";
|
||||
import { useRef, useState } from "react";
|
||||
import { Menu } from "@nutui/nutui-react-taro";
|
||||
import { Image, View } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import Bubble from "../Bubble";
|
||||
import "./index.scss";
|
||||
|
||||
const Demo3 = (props) => {
|
||||
const DistanceQuickFilter = (props) => {
|
||||
const {
|
||||
cityOptions,
|
||||
quickOptions,
|
||||
@@ -49,6 +49,7 @@ const Demo3 = (props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Menu
|
||||
className={`distanceQuickFilterWrap ${filterWrapperClassName}`}
|
||||
>
|
||||
@@ -105,6 +106,7 @@ const Demo3 = (props) => {
|
||||
</View>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default Demo3;
|
||||
export default DistanceQuickFilter;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { Popup } from "@nutui/nutui-react-taro";
|
||||
import Range from "../../components/Range";
|
||||
import Bubble from "../../components/Bubble";
|
||||
@@ -29,7 +29,7 @@ const FilterPopup = (props: FilterPopupProps) => {
|
||||
} = props;
|
||||
const store = useListStore() || {};
|
||||
const { getDictionaryValue } = useDictionaryActions() || {};
|
||||
const { timeBubbleData, gamesNum, dateRangeOptions } = store;
|
||||
const { timeBubbleData, gamesNum } = store;
|
||||
|
||||
/**
|
||||
* @description 处理字典选项
|
||||
@@ -91,7 +91,7 @@ const FilterPopup = (props: FilterPopupProps) => {
|
||||
round
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
style={{ marginTop: statusNavbarHeigh + "px" }}
|
||||
style={{ marginTop: statusNavbarHeigh + "px", maxHeight: '75vh' }}
|
||||
overlayStyle={{ marginTop: statusNavbarHeigh + "px" }}
|
||||
zIndex={1001}
|
||||
>
|
||||
|
||||
@@ -269,6 +269,15 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.localAreaWrapper {
|
||||
max-width: 69%;
|
||||
}
|
||||
|
||||
.localAreaTitle {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.smoothTitle {
|
||||
@@ -291,5 +300,11 @@
|
||||
.localArea {
|
||||
border: 0.5px solid #FFFFFFA6;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.localAreaText {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,21 @@ const ListCard: React.FC<ListCardProps> = ({
|
||||
image_list = [],
|
||||
court_type,
|
||||
key,
|
||||
participants, // 参与者图片
|
||||
venue_image_list, // 场馆图片
|
||||
venue_description,
|
||||
game_type, // 球局类型
|
||||
}) => {
|
||||
|
||||
// 参与者要前三个数据
|
||||
const participantsImageList = participants?.slice(0, 3) || [];
|
||||
// 场地 要第一个数据
|
||||
// const venueImageList = venue_image_list?.slice(0, 1) || [];
|
||||
const venueImage = venue_image_list?.[0]?.url || '';
|
||||
|
||||
// 是否显示畅打球局
|
||||
const isShowSmoothPlayingGame = game_type === '畅打球局';
|
||||
|
||||
const renderItemImage = (src: string) => {
|
||||
return (
|
||||
<Image
|
||||
@@ -32,7 +46,6 @@ const ListCard: React.FC<ListCardProps> = ({
|
||||
};
|
||||
|
||||
const handleViewDetail = () => {
|
||||
console.log("id", id);
|
||||
Taro.navigateTo({
|
||||
url: `/game_pages/detail/index?id=${id || 1}&from=list`,
|
||||
});
|
||||
@@ -120,11 +133,11 @@ const ListCard: React.FC<ListCardProps> = ({
|
||||
<View className="bottom-info">
|
||||
<View className="left-section">
|
||||
<View className="avatar-group">
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
{(participantsImageList || []).map((item, index) => (
|
||||
<View key={index} className="avatar">
|
||||
<Image
|
||||
className="avatar-image"
|
||||
src="https://images.unsplash.com/photo-1554068865-24cecd4e34b8?w=200&h=200&fit=crop&crop=center"
|
||||
src={item?.user?.avatar_url}
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</View>
|
||||
@@ -157,24 +170,24 @@ const ListCard: React.FC<ListCardProps> = ({
|
||||
<View className="image-section">{renderImages()}</View>
|
||||
</View>
|
||||
{/* 畅打球局 */}
|
||||
<View className="smoothPlayingGame">
|
||||
{isShowSmoothPlayingGame && <View className="smoothPlayingGame">
|
||||
<View className="smoothWrapper">
|
||||
<Image
|
||||
src={img.ICON_LIST_PLAYING_GAME}
|
||||
className="iconListPlayingGame"
|
||||
/>
|
||||
<Text className="smoothTitle">畅打球局</Text>
|
||||
<Text className="smoothTitle">{game_type}</Text>
|
||||
</View>
|
||||
<View className="line" />
|
||||
<View>场馆方:</View>
|
||||
<View className="localAreaTitle">场馆方:</View>
|
||||
<View className="localAreaWrapper">
|
||||
<Image
|
||||
className="localArea"
|
||||
src="https://images.unsplash.com/photo-1554068865-24cecd4e34b8?w=200&h=200&fit=crop&crop=center"
|
||||
src={venueImage}
|
||||
/>
|
||||
<Text className="localAreaText">仁恒河滨花园网球场</Text>
|
||||
<Text className="localAreaText">{venue_description}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,9 +2,8 @@ import { View } from "@tarojs/components";
|
||||
import ListCard from "@/components/ListCard";
|
||||
import ListLoadError from "@/components/ListLoadError";
|
||||
import ListCardSkeleton from "@/components/ListCardSkeleton";
|
||||
// import { VirtualList } from '@nutui/nutui-react-taro'
|
||||
import "./index.scss";
|
||||
import { useReachBottom } from "@tarojs/taro";
|
||||
import "./index.scss";
|
||||
|
||||
const ListContainer = (props) => {
|
||||
const {
|
||||
@@ -12,14 +11,12 @@ const ListContainer = (props) => {
|
||||
data = [],
|
||||
error,
|
||||
reload,
|
||||
recommendList,
|
||||
// recommendList,
|
||||
loadMoreMatches,
|
||||
} = props;
|
||||
console.log("===data", data);
|
||||
|
||||
useReachBottom(() => {
|
||||
console.log("触底了");
|
||||
// 调用 store 的加载更多方法
|
||||
// 加载更多方法
|
||||
loadMoreMatches();
|
||||
});
|
||||
|
||||
@@ -39,17 +36,6 @@ const ListContainer = (props) => {
|
||||
|
||||
// 渲染列表
|
||||
const renderList = (list) => {
|
||||
// 请求未回来显示骨架屏
|
||||
// if (loading && list?.length === 0) {
|
||||
// return (
|
||||
// <>
|
||||
// {new Array(10).fill(0).map(() => {
|
||||
// return <ListCardSkeleton />;
|
||||
// })}
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
|
||||
// 请求数据为空
|
||||
if (!loading && list?.length === 0) {
|
||||
return <ListLoadError reload={reload} text="暂无数据" />;
|
||||
@@ -58,15 +44,6 @@ const ListContainer = (props) => {
|
||||
// 渲染数据
|
||||
return (
|
||||
<>
|
||||
{/* <VirtualList
|
||||
containerHeight={1000}
|
||||
itemHeight={144}
|
||||
// itemEqual={false}
|
||||
list={list}
|
||||
itemRender={(data) => {
|
||||
return <ListCard {...data}/>
|
||||
}}
|
||||
/> */}
|
||||
{list?.map((match, index) => (
|
||||
<ListCard key={match.id || index} {...match} />
|
||||
))}
|
||||
@@ -76,15 +53,25 @@ const ListContainer = (props) => {
|
||||
|
||||
return (
|
||||
<View className="listContentWrapper">
|
||||
{renderList(data)}
|
||||
{/* 显示骨架屏 */}
|
||||
{loading && renderSkeleton()}
|
||||
{/* <View className="recommendTextWrapper">
|
||||
{/* <ScrollView
|
||||
scrollY
|
||||
scrollWithAnimation
|
||||
enableBackToTop
|
||||
enablePassive
|
||||
style={{ height: '100vh' }}
|
||||
onScrollToLower={handleScrollToLower}
|
||||
upperThreshold={60}
|
||||
> */}
|
||||
{renderList(data)}
|
||||
{/* 显示骨架屏 */}
|
||||
{loading && renderSkeleton()}
|
||||
{/* <View className="recommendTextWrapper">
|
||||
<Text className="recommendText">搜索结果较少,已为你推荐其他内容</Text>
|
||||
</View>
|
||||
{renderList(recommendList)} */}
|
||||
{/* 到底了 */}
|
||||
{data?.length > 0 && <View className="bottomTextWrapper">到底了</View>}
|
||||
{/* 到底了 */}
|
||||
{data?.length > 0 && <View className="bottomTextWrapper">到底了</View>}
|
||||
{/* </ScrollView> */}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -113,28 +113,31 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
/* 过渡动画设置,实现平滑切换 */
|
||||
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||
/* 启用硬件加速 */
|
||||
will-change: opacity, transform;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
|
||||
/* 优化过渡动画,使用更快的缓动函数 */
|
||||
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* 第一个元素样式 */
|
||||
.firstElement {
|
||||
// background-color: #4a90e2;
|
||||
}
|
||||
|
||||
/* 第二个元素样式 */
|
||||
.secondElement {
|
||||
// background-color: #5cb85c;
|
||||
/* 初始状态:透明且稍微偏移 */
|
||||
opacity: 0;
|
||||
// transform: translateY(20px);
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
/* 隐藏状态 */
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
// pointer-events: none; /* 隐藏时不响应鼠标事件 */
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
/* 可见状态 */
|
||||
|
||||
@@ -2,7 +2,6 @@ import { View, Text, Image } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
import { useUserInfo, } from '@/store/userStore'
|
||||
|
||||
import { useListState } from "@/store/listStore";
|
||||
import CustomNavbar from "@/components/CustomNavbar";
|
||||
import { Input } from "@nutui/nutui-react-taro";
|
||||
@@ -26,13 +25,11 @@ const ListHeader = (props: IProps) => {
|
||||
leftIconClick,
|
||||
} = config || {};
|
||||
const {
|
||||
location,
|
||||
getLocationText,
|
||||
getLocationLoading,
|
||||
statusNavbarHeightInfo,
|
||||
} = useGlobalState();
|
||||
const { gamesNum, searchValue, isShowInputCustomerNavBar } = useListState();
|
||||
const { statusBarHeight, navbarHeight } = statusNavbarHeightInfo;
|
||||
const { navbarHeight } = statusNavbarHeightInfo;
|
||||
|
||||
const { city,district } = useUserInfo()
|
||||
|
||||
@@ -68,6 +65,11 @@ const ListHeader = (props: IProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const navbarStyle = {
|
||||
height: `${navbarHeight}px`,
|
||||
paddingTop: `4px`,
|
||||
};
|
||||
|
||||
return (
|
||||
<CustomNavbar>
|
||||
<View className="listNavWrapper">
|
||||
@@ -75,10 +77,7 @@ const ListHeader = (props: IProps) => {
|
||||
<View
|
||||
className={`listNavContainer toggleElement firstElement hidden ${(!isShowInputCustomerNavBar && !showInput) && "visible"
|
||||
}`}
|
||||
style={{
|
||||
height: `${navbarHeight}px`,
|
||||
paddingTop: `${statusBarHeight}px`,
|
||||
}}
|
||||
style={navbarStyle}
|
||||
>
|
||||
<View className="listNavContentWrapper">
|
||||
{/* logo */}
|
||||
@@ -106,10 +105,7 @@ const ListHeader = (props: IProps) => {
|
||||
<View
|
||||
className={`inputCustomerNavbarContainer toggleElement secondElement hidden ${(isShowInputCustomerNavBar || showInput) && "visible"
|
||||
} ${showInput && "inputCustomerNavbarShowInput"}`}
|
||||
style={{
|
||||
height: `${navbarHeight}px`,
|
||||
paddingTop: `${statusBarHeight}px`,
|
||||
}}
|
||||
style={navbarStyle}
|
||||
>
|
||||
<View className="navContent">
|
||||
{/* logo */}
|
||||
@@ -137,4 +133,5 @@ const ListHeader = (props: IProps) => {
|
||||
</CustomNavbar>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListHeader;
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
gap: 5px;
|
||||
position: sticky;
|
||||
background-color: #fff;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.menuFilter {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import SearchBar from "@/components/SearchBar";
|
||||
import FilterPopup from "@/components/FilterPopup";
|
||||
import styles from "./index.module.scss";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import Taro, { usePageScroll } from "@tarojs/taro";
|
||||
import { useListStore } from "@/store/listStore";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
@@ -12,11 +12,6 @@ import ListContainer from "@/container/listContainer";
|
||||
import DistanceQuickFilter from "@/components/DistanceQuickFilter";
|
||||
import { withAuth } from "@/components";
|
||||
import { updateUserLocation } from "@/services/userService";
|
||||
// import img from "@/config/images";
|
||||
// import ShareCardCanvas from "@/components/ShareCardCanvas/example";
|
||||
|
||||
|
||||
|
||||
|
||||
const ListPage = () => {
|
||||
|
||||
@@ -46,23 +41,28 @@ const ListPage = () => {
|
||||
isShowInputCustomerNavBar,
|
||||
initialFilterSearch,
|
||||
loadMoreMatches,
|
||||
dateRangeOptions
|
||||
fetchGetGamesCount
|
||||
} = store;
|
||||
|
||||
// 简化的滚动处理函数
|
||||
usePageScroll((res) => {
|
||||
if (res?.scrollTop >= totalHeight) {
|
||||
!isShowInputCustomerNavBar &&
|
||||
const shouldShowInputNav = res?.scrollTop >= totalHeight;
|
||||
|
||||
// 只有当状态需要改变时才更新
|
||||
if (shouldShowInputNav && !isShowInputCustomerNavBar) {
|
||||
updateState({ isShowInputCustomerNavBar: true });
|
||||
} else {
|
||||
isShowInputCustomerNavBar &&
|
||||
} else if (!shouldShowInputNav && isShowInputCustomerNavBar) {
|
||||
updateState({ isShowInputCustomerNavBar: false });
|
||||
}
|
||||
});
|
||||
|
||||
const scrollContextRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
getLocation()
|
||||
}, []);
|
||||
|
||||
|
||||
// 监听距离和排序方式变化,自动调用接口
|
||||
useEffect(() => {
|
||||
// 只有当 distanceQuickFilter 有值时才调用接口
|
||||
@@ -89,6 +89,7 @@ const ListPage = () => {
|
||||
console.error('更新用户位置失败:', error);
|
||||
}
|
||||
}
|
||||
fetchGetGamesCount();
|
||||
|
||||
// 页面加载时获取数据
|
||||
getMatchesData();
|
||||
@@ -104,35 +105,45 @@ const ListPage = () => {
|
||||
// }
|
||||
|
||||
// 下拉刷新处理函数 - 使用Taro生命周期钩子
|
||||
Taro.usePullDownRefresh(() => {
|
||||
console.log("===触发下拉刷新");
|
||||
clearFilterOptions()
|
||||
Taro.usePullDownRefresh(async () => {
|
||||
try {
|
||||
// 调用刷新方法
|
||||
await refreshMatches();
|
||||
|
||||
// 调用 store 的刷新方法
|
||||
// refreshMatches()
|
||||
// .then(() => {
|
||||
// // 刷新完成后停止下拉刷新动画
|
||||
// Taro.stopPullDownRefresh();
|
||||
// 刷新完成后停止下拉刷新动画
|
||||
Taro.stopPullDownRefresh();
|
||||
|
||||
// // 显示刷新成功提示
|
||||
// Taro.showToast({
|
||||
// title: "刷新成功",
|
||||
// icon: "success",
|
||||
// duration: 1500,
|
||||
// });
|
||||
// })
|
||||
// .catch(() => {
|
||||
// // 刷新失败时也停止动画
|
||||
// Taro.stopPullDownRefresh();
|
||||
// 显示刷新成功提示
|
||||
Taro.showToast({
|
||||
title: "刷新成功",
|
||||
icon: "success",
|
||||
duration: 1500,
|
||||
});
|
||||
} catch (error) {
|
||||
// 刷新失败时也停止动画
|
||||
Taro.stopPullDownRefresh();
|
||||
|
||||
// Taro.showToast({
|
||||
// title: "刷新失败",
|
||||
// icon: "error",
|
||||
// duration: 1500,
|
||||
// });
|
||||
// });
|
||||
Taro.showToast({
|
||||
title: "刷新失败,请重试",
|
||||
icon: "error",
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 综合筛选确认
|
||||
* @returns
|
||||
*/
|
||||
const handleFilterConfirm = () => {
|
||||
toggleShowPopup();
|
||||
getMatchesData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 综合筛选弹框
|
||||
* @returns
|
||||
*/
|
||||
const toggleShowPopup = () => {
|
||||
updateState({ isShowFilterPopup: !isShowFilterPopup });
|
||||
};
|
||||
@@ -164,9 +175,10 @@ const ListPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View ref={scrollContextRef}>
|
||||
{/* 自定义导航 */}
|
||||
<CustomerNavBar />
|
||||
|
||||
{/* <ShareCardCanvas /> */}
|
||||
<View className={styles.listPage}>
|
||||
<View
|
||||
@@ -187,7 +199,7 @@ const ListPage = () => {
|
||||
<FilterPopup
|
||||
loading={loading}
|
||||
onCancel={toggleShowPopup}
|
||||
onConfirm={toggleShowPopup}
|
||||
onConfirm={handleFilterConfirm}
|
||||
onChange={handleUpdateFilterOptions}
|
||||
filterOptions={filterOptions}
|
||||
onClear={clearFilterOptions}
|
||||
@@ -199,7 +211,10 @@ const ListPage = () => {
|
||||
)}
|
||||
</View>
|
||||
{/* 筛选 */}
|
||||
<View className={styles.listTopFilterWrapper}>
|
||||
<View className={styles.listTopFilterWrapper}
|
||||
style={{
|
||||
top: totalHeight -1,
|
||||
}}>
|
||||
<DistanceQuickFilter
|
||||
cityOptions={distanceData}
|
||||
quickOptions={quickFilterData}
|
||||
@@ -221,7 +236,6 @@ const ListPage = () => {
|
||||
loadMoreMatches={loadMoreMatches}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<GuideBar currentPage="list" />
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -38,16 +38,9 @@ const SearchResult = () => {
|
||||
const pages = Taro.getCurrentPages()
|
||||
const currentPage = pages?.[pages.length - 1];
|
||||
updateState({currentPage, isSearchResult: true})
|
||||
// if (location?.address) {
|
||||
// 保存位置
|
||||
// updateState({ location });
|
||||
// 页面加载时获取数据
|
||||
console.log('===搜索结果页===')
|
||||
getMatchesData();
|
||||
// }
|
||||
|
||||
return () => {
|
||||
console.log('===搜索结果组件卸载')
|
||||
updateState({currentPage: '', isSearchResult: false})
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -46,6 +46,20 @@ export const getGamesIntegrateList = async (params?: Record<string, any>) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取列表数量
|
||||
* @param params
|
||||
* @returns
|
||||
*/
|
||||
export const getGamesCount = async (params?: Record<string, any>) => {
|
||||
try {
|
||||
return httpService.post('/games/count', params, { showLoading: true })
|
||||
} catch (error) {
|
||||
console.error("列表数量获取失败:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取搜索历史记录的异步函数
|
||||
* @param {Object} params - 查询参数对象
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
getSearchHistory,
|
||||
clearHistory,
|
||||
searchSuggestion,
|
||||
getGamesCount,
|
||||
} from "../services/listApi";
|
||||
import {
|
||||
ListActions,
|
||||
@@ -14,12 +15,10 @@ import {
|
||||
IPayload,
|
||||
} from "../../types/list/types";
|
||||
|
||||
import dateRangeUtils from '@/utils/dateRange'
|
||||
|
||||
// 完整的 Store 类型
|
||||
type TennisStore = ListState & ListActions;
|
||||
|
||||
const toDate = dateRangeUtils?.formatDate(new Date())
|
||||
const defaultDateRange: [string, string] = [dayjs().format('YYYY-MM-DD'), dayjs().add(1, 'M').format('YYYY-MM-DD')]
|
||||
|
||||
const defaultFilterOptions: IFilterOptions = {
|
||||
@@ -100,6 +99,8 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
],
|
||||
// 球局数量
|
||||
gamesNum: 0,
|
||||
// 是否还有更多数据
|
||||
isHasMoreData: true,
|
||||
// 页面滚动距离顶部距离 是否大于0
|
||||
isScrollTop: false,
|
||||
// 搜索历史数据
|
||||
@@ -143,22 +144,31 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
lat: state?.location?.latitude,
|
||||
lng: state?.location?.longitude,
|
||||
};
|
||||
console.log('===列表参数params', params)
|
||||
return params;
|
||||
},
|
||||
|
||||
// 设置搜索的列表结果
|
||||
setListData: (payload: IPayload) => {
|
||||
setListData: (payload: IPayload & { isAppend?: boolean }) => {
|
||||
const { isSearchResult } = get();
|
||||
const { error, data, loading, gamesNum } = payload;
|
||||
const { error, data, loading, count, isAppend = false } = payload;
|
||||
const saveKey = isSearchResult ? "searchResultData" : "matches";
|
||||
const saveData = { error, loading, gamesNum, [saveKey]: data };
|
||||
console.log('===saveData', saveData)
|
||||
set({...saveData});
|
||||
const isHasMoreData = count > 0;
|
||||
|
||||
if (isAppend) {
|
||||
// 追加数据到现有数组
|
||||
const currentData = get()[saveKey] || [];
|
||||
const newData = [...currentData, ...(data || [])];
|
||||
const saveData = { error, loading, count, isHasMoreData, [saveKey]: newData };
|
||||
set({...saveData});
|
||||
} else {
|
||||
// 替换整个数组
|
||||
const saveData = { error, loading, count, isHasMoreData, [saveKey]: data };
|
||||
set({...saveData});
|
||||
}
|
||||
},
|
||||
|
||||
// 获取列表数据(常规搜索)
|
||||
fetchMatches: async (params, isFirstLoad = false) => {
|
||||
fetchMatches: async (params, isFirstLoad = false, isAppend = false) => {
|
||||
set({ loading: true, error: null });
|
||||
const { getSearchParams, setListData, distanceQuickFilter } = get();
|
||||
|
||||
@@ -191,70 +201,45 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
error: "-1",
|
||||
data: [],
|
||||
loading: false,
|
||||
gamesNum: 0,
|
||||
count: 0,
|
||||
isAppend,
|
||||
});
|
||||
return Promise.reject(new Error('获取数据失败'));
|
||||
}
|
||||
const { count, rows } = data;
|
||||
setListData({
|
||||
error: '',
|
||||
data: rows || [],
|
||||
loading: false,
|
||||
gamesNum: count,
|
||||
count,
|
||||
isAppend,
|
||||
});
|
||||
return Promise.resolve();
|
||||
} catch (error) {
|
||||
setListData({
|
||||
error: "-1",
|
||||
data: [],
|
||||
loading: false,
|
||||
gamesNum: 0,
|
||||
count: 0,
|
||||
isAppend,
|
||||
});
|
||||
return Promise.reject(error);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取列表数据(智能筛选)
|
||||
// getIntegrateListData: async (params) => {
|
||||
// set({ loading: true, error: null });
|
||||
// const { getSearchParams, setListData } = get();
|
||||
// try {
|
||||
// const searchParams = getSearchParams() || {};
|
||||
// const reqParams = {
|
||||
// ...(searchParams || {}),
|
||||
// ...params,
|
||||
// };
|
||||
// reqParams.order = "";
|
||||
// console.log("===getGamesIntegrateList 获取列表数据参数:", reqParams);
|
||||
// const resData = (await getGamesIntegrateList(reqParams)) || {};
|
||||
// const { data = {}, code } = resData;
|
||||
// if (code !== 0) {
|
||||
// setListData({
|
||||
// error: '-1',
|
||||
// data: [],
|
||||
// loading: false,
|
||||
// gamesNum: 0,
|
||||
// });
|
||||
// }
|
||||
// const { count, rows } = data;
|
||||
// setListData({
|
||||
// // recommendList: rows || [],
|
||||
// error: null,
|
||||
// data: rows || [],
|
||||
// loading: false,
|
||||
// gamesNum: count,
|
||||
// });
|
||||
// } catch (error) {
|
||||
// setListData({
|
||||
// error: null,
|
||||
// matches: [],
|
||||
// loading: false,
|
||||
// gamesNum: 0,
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
|
||||
// 获取列表数据
|
||||
getMatchesData: () => {
|
||||
getMatchesData: async () => {
|
||||
const { fetchMatches } = get();
|
||||
fetchMatches({}, true); // 第一次进入页面,传入 isFirstLoad = true
|
||||
return await fetchMatches({}, true); // 第一次进入页面,传入 isFirstLoad = true
|
||||
},
|
||||
|
||||
// 获取球局数量
|
||||
fetchGetGamesCount: async () => {
|
||||
const { getSearchParams } = get();
|
||||
const params = getSearchParams() || {};
|
||||
const resData = (await getGamesCount(params)) || {};
|
||||
const gamesNum = resData?.data?.count || 0;
|
||||
set({ gamesNum });
|
||||
},
|
||||
|
||||
// 获取历史搜索数据
|
||||
@@ -307,7 +292,7 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
|
||||
// 更新综合筛选项
|
||||
updateFilterOptions: (payload: Record<string, any>) => {
|
||||
const { filterOptions: preFilterOptions, getMatchesData } = get() || {};
|
||||
const { filterOptions: preFilterOptions, fetchGetGamesCount } = get() || {};
|
||||
const filterOptions = { ...preFilterOptions, ...payload };
|
||||
const filterCount = Object.values(filterOptions).filter(Boolean).length;
|
||||
console.log("===更新综合筛选项", filterOptions, filterCount);
|
||||
@@ -316,42 +301,48 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
filterCount,
|
||||
pageOption: defaultPageOption,
|
||||
});
|
||||
// 重新搜索数据
|
||||
getMatchesData();
|
||||
// 获取球局数量
|
||||
fetchGetGamesCount();
|
||||
},
|
||||
|
||||
// 清空综合筛选选项
|
||||
clearFilterOptions: () => {
|
||||
const { getMatchesData } = get() || {};
|
||||
const { getMatchesData, fetchGetGamesCount } = get() || {};
|
||||
set({
|
||||
filterOptions: defaultFilterOptions,
|
||||
filterCount: 0,
|
||||
pageOption: defaultPageOption,
|
||||
});
|
||||
getMatchesData();
|
||||
fetchGetGamesCount();
|
||||
},
|
||||
|
||||
// 加载更多数据
|
||||
loadMoreMatches: () => {
|
||||
const { pageOption, getMatchesData } = get() || {};
|
||||
const { pageOption, fetchMatches, isHasMoreData } = get() || {};
|
||||
if (!isHasMoreData) {
|
||||
return;
|
||||
}
|
||||
set({
|
||||
pageOption: {
|
||||
page: pageOption?.page + 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
});
|
||||
getMatchesData();
|
||||
// 加载更多时追加数据到现有数组
|
||||
fetchMatches({}, false, true);
|
||||
},
|
||||
|
||||
// 初始化搜索条件 重新搜索
|
||||
initialFilterSearch: () => {
|
||||
const { getMatchesData } = get();
|
||||
initialFilterSearch: async () => {
|
||||
const { getMatchesData, fetchGetGamesCount } = get();
|
||||
set({
|
||||
distanceQuickFilter: defaultDistanceQuickFilter,
|
||||
filterOptions: defaultFilterOptions,
|
||||
pageOption: defaultPageOption,
|
||||
});
|
||||
getMatchesData();
|
||||
fetchGetGamesCount();
|
||||
return await getMatchesData();
|
||||
},
|
||||
|
||||
// 更新store数据
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface ListState {
|
||||
timeBubbleData: BubbleOption[]
|
||||
dateRangeOptions: BubbleOption[]
|
||||
gamesNum: number
|
||||
isHasMoreData: boolean
|
||||
isScrollTop: boolean
|
||||
searchHistoryParams: Record<string, any>
|
||||
searchHistory: {id: number, title: string}[]
|
||||
@@ -61,7 +62,7 @@ export interface ListState {
|
||||
}
|
||||
|
||||
export interface ListActions {
|
||||
fetchMatches: (params?: Record<string, any>,isFirstLoad?: Boolean) => Promise<void>
|
||||
fetchMatches: (params?: Record<string, any>, isFirstLoad?: Boolean, isAppend?: boolean) => Promise<void>
|
||||
// getIntegrateListData: (params?: Record<string, any>) => Promise<void>
|
||||
getMatchesData: () => void
|
||||
clearError: () => void
|
||||
@@ -75,13 +76,15 @@ export interface ListActions {
|
||||
loadMoreMatches: () => void
|
||||
initialFilterSearch: () => void
|
||||
setListData: (payload: IPayload) => void
|
||||
fetchGetGamesCount: () => Promise<void>
|
||||
}
|
||||
|
||||
export interface IPayload {
|
||||
error: string;
|
||||
data: TennisMatch[];
|
||||
loading: boolean;
|
||||
gamesNum: number;
|
||||
count: number;
|
||||
isAppend?: boolean;
|
||||
}
|
||||
|
||||
// 快捷筛选
|
||||
@@ -178,4 +181,15 @@ export interface ListCardProps {
|
||||
shinei: string;
|
||||
showSkeleton?: boolean;
|
||||
key?: string
|
||||
participants: {
|
||||
user: {
|
||||
avatar_url: string;
|
||||
};
|
||||
}[];
|
||||
venue_image_list: {
|
||||
url: string;
|
||||
}[];
|
||||
venue_description: string;
|
||||
game_type: string;
|
||||
type: string;
|
||||
}
|
||||
Reference in New Issue
Block a user