This commit is contained in:
李瑞
2025-09-14 01:18:23 +08:00
parent c44bd01613
commit 01aad920ad
17 changed files with 569 additions and 352 deletions

View File

@@ -5,8 +5,6 @@ import "qweather-icons/font/qweather-icons.css";
import { useDictionaryStore } from "./store/dictionaryStore"; import { useDictionaryStore } from "./store/dictionaryStore";
import { useGlobalStore } from "./store/global"; import { useGlobalStore } from "./store/global";
// import { getNavbarHeight } from "@/utils/getNavbarHeight";
interface AppProps { interface AppProps {
children: ReactNode; children: ReactNode;
} }

View File

@@ -1,8 +1,10 @@
.customerNavbar { .customerNavbar {
position: sticky; position: fixed;
top: 0; top: 0;
left: 0;
z-index: 999; z-index: 999;
background-color: #ffffff;
overflow: hidden; overflow: hidden;
z-index: 999; z-index: 999;
width: 100%;
background-color: #fff;
} }

View File

@@ -9,12 +9,12 @@ interface IProps {
const CustomNavbar = (props: IProps) => { const CustomNavbar = (props: IProps) => {
const { children } = props; const { children } = props;
const { statusNavbarHeightInfo } = useGlobalState(); const { statusNavbarHeightInfo } = useGlobalState();
const { statusBarHeight, navbarHeight } = statusNavbarHeightInfo; const { statusBarHeight, navBarHeight } = statusNavbarHeightInfo;
return ( return (
<View <View
className={styles.customerNavbar} className={styles.customerNavbar}
style={{ height: `${navbarHeight}px`, paddingTop: `${statusBarHeight}px`, }} style={{ height: `${navBarHeight}px`, paddingTop: `${statusBarHeight}px`, }}
> >
{children} {children}
</View> </View>

View File

@@ -6,7 +6,7 @@ import Taro from "@tarojs/taro";
const ListHeader = () => { const ListHeader = () => {
const { statusNavbarHeightInfo } = useGlobalState(); const { statusNavbarHeightInfo } = useGlobalState();
const { statusBarHeight, navbarHeight, totalHeight } = statusNavbarHeightInfo; const { statusBarHeight, navBarHeight, totalHeight } = statusNavbarHeightInfo;
const handleBack = () => { const handleBack = () => {
Taro.navigateBack(); Taro.navigateBack();
} }
@@ -19,7 +19,7 @@ const ListHeader = () => {
<View <View
className={styles.container} className={styles.container}
style={{ style={{
height: `${navbarHeight}px`, height: `${navBarHeight}px`,
paddingTop: `${statusBarHeight}px`, paddingTop: `${statusBarHeight}px`,
}} }}
> >

View File

@@ -4,7 +4,7 @@
--nutui-searchbar-content-border-radius: 44px; --nutui-searchbar-content-border-radius: 44px;
--nutui-searchbar-input-text-color: #000000; --nutui-searchbar-input-text-color: #000000;
--nutui-searchbar-input-padding: 0 0 0 10px; --nutui-searchbar-input-padding: 0 0 0 10px;
--nutui-searchbar-padding: 10px 0 0 0; --nutui-searchbar-padding: 10px 0 10px 0;
background-color: unset; background-color: unset;
:global(.nut-searchbar-content) { :global(.nut-searchbar-content) {

View File

@@ -1,6 +1,6 @@
.inputCustomerNavbarContainer { .inputCustomerNavbarContainer {
padding-left: 17px;
display: flex; display: flex;
align-items: center;
gap: 8px; gap: 8px;
@@ -15,6 +15,7 @@
} }
.navContent { .navContent {
padding-left: 17px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 4px; gap: 4px;

View File

@@ -1,8 +1,6 @@
import { View, Image } from "@tarojs/components"; import { View, Image } from "@tarojs/components";
import img from "@/config/images"; import img from "@/config/images";
import { getCurrentLocation } from "@/utils/locationUtils";
import "./index.scss"; import "./index.scss";
import { useEffect } from "react";
import { useGlobalState } from "@/store/global"; import { useGlobalState } from "@/store/global";
import { useListState } from "@/store/listStore"; import { useListState } from "@/store/listStore";
import CustomNavbar from "@/components/CustomNavbar"; import CustomNavbar from "@/components/CustomNavbar";
@@ -15,25 +13,9 @@ interface IProps {
const ListHeader = (props: IProps) => { const ListHeader = (props: IProps) => {
const { icon } = props; const { icon } = props;
const { updateState, statusNavbarHeightInfo } = useGlobalState(); const { statusNavbarHeightInfo } = useGlobalState();
const { searchValue } = useListState(); const { searchValue } = useListState();
const { statusBarHeight, navbarHeight } = statusNavbarHeightInfo; const { statusBarHeight, navBarHeight } = statusNavbarHeightInfo;
// 获取位置信息
// const getCurrentLocal = () => {
// updateState({
// getLocationLoading: true,
// });
// getCurrentLocation().then((res) => {
// updateState({
// getLocationLoading: false,
// location: res || {},
// });
// });
// };
// useEffect(() => {
// getCurrentLocal();
// }, []);
const handleInputClick = () => { const handleInputClick = () => {
Taro.navigateTo({ Taro.navigateTo({
@@ -46,7 +28,7 @@ const ListHeader = (props: IProps) => {
<View <View
className="inputCustomerNavbarContainer" className="inputCustomerNavbarContainer"
style={{ style={{
height: `${navbarHeight}px`, height: `${navBarHeight}px`,
paddingTop: `${statusBarHeight}px`, paddingTop: `${statusBarHeight}px`,
}} }}
> >

View File

@@ -5,6 +5,7 @@
flex-direction: column; flex-direction: column;
gap: 5px; gap: 5px;
padding-bottom: 34px; padding-bottom: 34px;
min-height: 100vh;
.recommendTextWrapper { .recommendTextWrapper {
display: flex; display: flex;

View File

@@ -3,6 +3,9 @@
} }
.listNavContainer { .listNavContainer {
display: flex;
align-items: center;
.listNavLine { .listNavLine {
width: 1px; width: 1px;
height: 25px; height: 25px;
@@ -51,6 +54,7 @@
.inputCustomerNavbarContainer { .inputCustomerNavbarContainer {
padding-left: 17px; padding-left: 17px;
display: flex; display: flex;
align-items: center;
gap: 8px; gap: 8px;
.logo { .logo {
@@ -99,6 +103,7 @@
.inputCustomerNavbarShowInput { .inputCustomerNavbarShowInput {
padding-left: 10px; padding-left: 10px;
.logo { .logo {
width: 32px; width: 32px;
height: 32px; height: 32px;
@@ -120,24 +125,23 @@
/* 优化过渡动画,使用更平滑的缓动函数和更短的动画时间 */ /* 优化过渡动画,使用更平滑的缓动函数和更短的动画时间 */
transition: opacity 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94), transition: opacity 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94),
transform 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94); transform 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
} }
/* 第一个元素样式 */ /* 第一个元素样式 */
.firstElement { .firstElement {}
}
/* 第二个元素样式 */ /* 第二个元素样式 */
.secondElement { .secondElement {
/* 初始状态:透明且稍微偏移 */ /* 初始状态:透明且稍微偏移 */
opacity: 0; opacity: 0;
transform: translateY(5px); transform: translateY(-5px);
} }
/* 隐藏状态 */ /* 隐藏状态 */
.hidden { .hidden {
opacity: 0; opacity: 0;
transform: translateY(5px); transform: translateY(-5px);
} }
/* 可见状态 */ /* 可见状态 */

View File

@@ -7,14 +7,13 @@ import CustomNavbar from "@/components/CustomNavbar";
import { Input } from "@nutui/nutui-react-taro"; import { Input } from "@nutui/nutui-react-taro";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import "./index.scss"; import "./index.scss";
import { useMemo } from "react";
interface IProps { interface IProps {
config?: { config?: {
showInput: boolean; showInput: boolean;
inputLeftIcon: string; inputLeftIcon?: string;
iconPath?: string; iconPath?: string;
leftIconClick: () => void; leftIconClick?: () => void;
}; };
} }
@@ -29,10 +28,12 @@ const ListHeader = (props: IProps) => {
getLocationLoading, getLocationLoading,
statusNavbarHeightInfo, statusNavbarHeightInfo,
} = useGlobalState(); } = useGlobalState();
const { gamesNum, searchValue, isShowInputCustomerNavBar } = useListState(); const { gamesNum, searchValue } = useListState();
const { navbarHeight } = statusNavbarHeightInfo; const { navBarHeight } = statusNavbarHeightInfo;
const { city,district } = useUserInfo() const userInfo = useUserInfo()
const city = (userInfo as any)?.city || ''
const district = (userInfo as any)?.district || ''
console.log("useUserInfo",city,district ) console.log("useUserInfo",city,district )
@@ -67,23 +68,17 @@ const ListHeader = (props: IProps) => {
}; };
const navbarStyle = { const navbarStyle = {
height: `${navbarHeight}px`, height: `${navBarHeight}px`,
paddingTop: `4px`,
}; };
const showInputNavBar = useMemo(() => {
return isShowInputCustomerNavBar || showInput;
}, [isShowInputCustomerNavBar, showInput]);
console.log("===showInputNavBar",showInputNavBar)
return ( return (
<CustomNavbar> <CustomNavbar>
<View className="listNavWrapper"> <View className="listNavWrapper">
{/* 首页logo 导航*/} {/* 首页logo 导航*/}
<View <View
className={`listNavContainer toggleElement firstElement hidden ${(!showInputNavBar && !showInput) && "visible" className={`listNavContainer toggleElement firstElement hidden
}`} ${!showInput ? "visible" : ""}`}
style={navbarStyle} style={navbarStyle}
> >
<View className="listNavContentWrapper"> <View className="listNavContentWrapper">
@@ -110,8 +105,8 @@ const ListHeader = (props: IProps) => {
</View> </View>
{/* 搜索导航 */} {/* 搜索导航 */}
<View <View
className={`inputCustomerNavbarContainer toggleElement secondElement hidden ${(showInputNavBar || showInput) && "visible" className={`inputCustomerNavbarContainer toggleElement secondElement hidden ${showInput && "visible"
} ${showInput && "inputCustomerNavbarShowInput"}`} } ${showInput ? "inputCustomerNavbarShowInput" : ""}`}
style={navbarStyle} style={navbarStyle}
> >
<View className="navContent"> <View className="navContent">
@@ -128,8 +123,9 @@ const ListHeader = (props: IProps) => {
src={img.ICON_LIST_SEARCH_SEARCH} src={img.ICON_LIST_SEARCH_SEARCH}
/> />
<Input <Input
placeholder="搜索上海的球局和场地" placeholder="搜索上球局和场地"
clearable={false} // clearable={false}
disabled
value={searchValue} value={searchValue}
onClick={handleInputClick} onClick={handleInputClick}
/> />

View File

@@ -22,28 +22,32 @@ const ListPage = () => {
const { totalHeight } = statusNavbarHeightInfo || {}; const { totalHeight } = statusNavbarHeightInfo || {};
const { const {
isShowFilterPopup, listPageState,
error,
matches,
recommendList,
loading, loading,
getMatchesData, error,
updateState, searchValue,
filterCount,
updateFilterOptions, // 更新筛选条件
filterOptions,
clearFilterOptions,
distanceData, distanceData,
quickFilterData, quickFilterData,
distanceQuickFilter, getMatchesData,
isScrollTop, updateState,
searchValue, updateListPageState,
isShowInputCustomerNavBar, updateFilterOptions, // 更新筛选条件
clearFilterOptions,
initialFilterSearch, initialFilterSearch,
loadMoreMatches, loadMoreMatches,
fetchGetGamesCount fetchGetGamesCount
} = store; } = store;
const {
isShowFilterPopup,
data: matches,
recommendList,
filterCount,
filterOptions,
distanceQuickFilter,
isShowInputCustomerNavBar,
} = listPageState || {};
// 防抖的滚动处理函数 // 防抖的滚动处理函数
const handleScroll = useCallback((res) => { const handleScroll = useCallback((res) => {
const currentScrollTop = res?.scrollTop || 0; const currentScrollTop = res?.scrollTop || 0;
@@ -62,9 +66,13 @@ const ListPage = () => {
scrollTimeoutRef.current = setTimeout(() => { scrollTimeoutRef.current = setTimeout(() => {
// 只有在状态真正需要改变时才更新 // 只有在状态真正需要改变时才更新
if (shouldShowInputNav && !isShowInputCustomerNavBar) { if (shouldShowInputNav && !isShowInputCustomerNavBar) {
updateState({ isShowInputCustomerNavBar: true }); updateListPageState({
isShowInputCustomerNavBar: true
});
} else if (shouldHideInputNav && isShowInputCustomerNavBar) { } else if (shouldHideInputNav && isShowInputCustomerNavBar) {
updateState({ isShowInputCustomerNavBar: false }); updateListPageState({
isShowInputCustomerNavBar: false
});
} }
lastScrollTopRef.current = currentScrollTop; lastScrollTopRef.current = currentScrollTop;
@@ -173,7 +181,9 @@ const ListPage = () => {
* @returns * @returns
*/ */
const toggleShowPopup = () => { const toggleShowPopup = () => {
updateState({ isShowFilterPopup: !isShowFilterPopup }); updateListPageState({
isShowFilterPopup: !isShowFilterPopup
});
}; };
/** /**
@@ -188,7 +198,7 @@ const ListPage = () => {
// 距离筛选 // 距离筛选
const handleDistanceOrQuickChange = (name, value) => { const handleDistanceOrQuickChange = (name, value) => {
updateState({ updateListPageState({
distanceQuickFilter: { distanceQuickFilter: {
...distanceQuickFilter, ...distanceQuickFilter,
[name]: value, [name]: value,
@@ -203,69 +213,75 @@ const ListPage = () => {
}; };
return ( return (
<View ref={scrollContextRef}> <>
{/* 自定义导航 */} {/* 自定义导航 */}
<CustomerNavBar /> <CustomerNavBar
config={{
{/* <ShareCardCanvas /> */} showInput: isShowInputCustomerNavBar,
<View className={styles.listPage}> }}
<View />
className={`${styles.listTopSearchWrapper} ${isScrollTop ? styles.isScroll : "" <View ref={scrollContextRef}>
}`} {/* <ShareCardCanvas /> */}
> {/* 综合筛选 */}
<SearchBar {isShowFilterPopup && (
handleFilterIcon={toggleShowPopup} <View>
isSelect={filterCount > 0} <FilterPopup
filterCount={filterCount} loading={loading}
onChange={handleSearchChange} onCancel={toggleShowPopup}
value={searchValue} onConfirm={handleFilterConfirm}
onInputClick={handleSearchClick} onChange={handleUpdateFilterOptions}
/> filterOptions={filterOptions}
{/* 综合筛选 */} onClear={clearFilterOptions}
{isShowFilterPopup && ( visible={isShowFilterPopup}
<View> onClose={toggleShowPopup}
<FilterPopup statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight}
loading={loading} />
onCancel={toggleShowPopup} </View>
onConfirm={handleFilterConfirm} )}
onChange={handleUpdateFilterOptions}
filterOptions={filterOptions}
onClear={clearFilterOptions}
visible={isShowFilterPopup}
onClose={toggleShowPopup}
statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight}
/>
</View>
)}
</View>
{/* 筛选 */}
<View className={styles.listTopFilterWrapper}
style={{
top: totalHeight -1,
}}>
<DistanceQuickFilter
cityOptions={distanceData}
quickOptions={quickFilterData}
onChange={handleDistanceOrQuickChange}
cityName="distance"
quickName="quick"
cityValue={distanceQuickFilter?.distance}
quickValue={distanceQuickFilter?.quick}
/>
</View>
{/* 列表内容 */} {/* 列表内容 */}
<ListContainer <View className={styles.listPage} style={{ paddingTop: totalHeight }}>
data={matches} <View
recommendList={recommendList} className={`${styles.listTopSearchWrapper}`}
loading={loading} >
error={error} <SearchBar
reload={refreshMatches} handleFilterIcon={toggleShowPopup}
loadMoreMatches={loadMoreMatches} isSelect={filterCount > 0}
/> filterCount={filterCount}
onChange={handleSearchChange}
value={searchValue}
onInputClick={handleSearchClick}
/>
</View>
{/* 筛选 */}
<View className={styles.listTopFilterWrapper}
style={{
top: totalHeight - 1,
}}>
<DistanceQuickFilter
cityOptions={distanceData}
quickOptions={quickFilterData}
onChange={handleDistanceOrQuickChange}
cityName="distance"
quickName="quick"
cityValue={distanceQuickFilter?.distance}
quickValue={distanceQuickFilter?.quick}
/>
</View>
{/* 列表内容 */}
<ListContainer
data={matches}
recommendList={recommendList}
loading={loading}
error={error}
reload={refreshMatches}
loadMoreMatches={loadMoreMatches}
/>
</View>
</View> </View>
<GuideBar currentPage="list" /> <GuideBar currentPage="list" />
</View> </>
); );
}; };

View File

@@ -1,4 +1,3 @@
// import CustomerNavbarBack from "@/components/CustomerNavbarBack";
import { View, Image, Text } from "@tarojs/components"; import { View, Image, Text } from "@tarojs/components";
import { Input } from "@nutui/nutui-react-taro"; import { Input } from "@nutui/nutui-react-taro";
import { useEffect, useMemo, useRef } from "react"; import { useEffect, useMemo, useRef } from "react";
@@ -12,13 +11,17 @@ const ListSearch = () => {
const { const {
searchValue, searchValue,
updateState, updateState,
updateSearchPageState,
getSearchHistory, getSearchHistory,
searchHistory = [],
clearHistory, clearHistory,
searchSuggestion, searchSuggestion,
} = useListState() || {};
const {
searchHistory = [],
suggestionList, suggestionList,
isShowSuggestion, isShowSuggestion,
} = useListState() || {}; } = useListState()?.searchPageState || {};
const ref = useRef<any>(null); const ref = useRef<any>(null);
useEffect(() => { useEffect(() => {
@@ -53,7 +56,7 @@ const ListSearch = () => {
if (value) { if (value) {
searchSuggestion(value); searchSuggestion(value);
} else { } else {
updateState({ updateSearchPageState({
isShowSuggestion: false, isShowSuggestion: false,
}); });
} }
@@ -63,7 +66,10 @@ const ListSearch = () => {
* @description 点击清空输入内容 * @description 点击清空输入内容
*/ */
const handleClear = () => { const handleClear = () => {
updateState({ searchValue: "", isShowSuggestion: false }); updateState({ searchValue: "" });
updateSearchPageState({
isShowSuggestion: false
});
}; };
/** /**
@@ -86,8 +92,8 @@ const ListSearch = () => {
* @description 点击联想词 * @description 点击联想词
*/ */
const handleSuggestionSearch = (val: string) => { const handleSuggestionSearch = (val: string) => {
updateState({ updateState({ searchValue: val });
searchValue: val, updateSearchPageState({
isShowSuggestion: false, isShowSuggestion: false,
}); });
handleSearch(val); handleSearch(val);

View File

@@ -13,22 +13,27 @@ import Taro from "@tarojs/taro";
const SearchResult = () => { const SearchResult = () => {
const { const {
isShowFilterPopup, searchPageState,
error,
distanceQuickFilter,
searchResultData,
recommendList,
loading, loading,
updateState, error,
filterCount,
updateFilterOptions, // 更新筛选条件
filterOptions,
clearFilterOptions,
distanceData, distanceData,
quickFilterData, quickFilterData,
updateState,
updateSearchPageState,
updateFilterOptions, // 更新筛选条件
clearFilterOptions,
loadMoreMatches, loadMoreMatches,
getMatchesData getMatchesData
} = useListStore() || {}; } = useListStore() || {};
const {
isShowFilterPopup,
distanceQuickFilter,
searchResultData,
recommendList,
filterCount,
filterOptions,
} = searchPageState || {};
const { statusNavbarHeightInfo } = useGlobalState() || {}; const { statusNavbarHeightInfo } = useGlobalState() || {};
const { totalHeight } = statusNavbarHeightInfo || {}; const { totalHeight } = statusNavbarHeightInfo || {};
const isSelect = filterCount > 0; const isSelect = filterCount > 0;
@@ -36,11 +41,17 @@ const SearchResult = () => {
useEffect(() => { useEffect(() => {
const pages = Taro.getCurrentPages() const pages = Taro.getCurrentPages()
const currentPage = pages?.[pages.length - 1]; const currentPage = pages?.[pages.length - 1];
updateState({currentPage, isSearchResult: true}) updateState({
getMatchesData(); currentPage,
isSearchResult: true
})
getMatchesData();
return () => { return () => {
updateState({currentPage: '', isSearchResult: false}) updateState({
currentPage: '',
isSearchResult: false
})
} }
}, []); }, []);
@@ -53,12 +64,23 @@ const SearchResult = () => {
}; };
const toggleShowPopup = () => { const toggleShowPopup = () => {
updateState({ isShowFilterPopup: !isShowFilterPopup }); updateSearchPageState({
isShowFilterPopup: !isShowFilterPopup
});
}; };
/**
* @description 综合筛选确认
* @returns
*/
const handleFilterConfirm = () => {
toggleShowPopup();
getMatchesData();
}
// 距离筛选 // 距离筛选
const handleDistanceOrQuickChange = (name, value) => { const handleDistanceOrQuickChange = (name, value) => {
updateState({ updateSearchPageState({
distanceQuickFilter: { distanceQuickFilter: {
...distanceQuickFilter, ...distanceQuickFilter,
[name]: value, [name]: value,
@@ -75,7 +97,7 @@ const SearchResult = () => {
} }
return ( return (
<View className="searchResultPage"> <>
{/* 自定义导航 */} {/* 自定义导航 */}
<CustomerNavBar <CustomerNavBar
config={{ config={{
@@ -84,65 +106,65 @@ const SearchResult = () => {
leftIconClick: handleLeftIconClick leftIconClick: handleLeftIconClick
}} }}
/> />
{/* 筛选 */} <View className="searchResultPage" style={{ paddingTop: totalHeight }}>
<View {/* 筛选 */}
className="searchResultFilterWrapper"
style={
{
top: `${totalHeight}px`
}
}
>
<DistanceQuickFilter
cityOptions={distanceData}
quickOptions={quickFilterData}
onChange={handleDistanceOrQuickChange}
cityName="distance"
quickName="quick"
cityValue={distanceQuickFilter?.distance}
quickValue={distanceQuickFilter?.quick}
/>
{/* 筛选 icon */}
<View <View
className={`filterIconWrapper ${isSelect && "active"}`} className="searchResultFilterWrapper"
onClick={toggleShowPopup} style={
{
top: `${totalHeight}px`
}
}
> >
<Image <DistanceQuickFilter
src={isSelect ? img.ICON_FILTER_SELECTED : img.ICON_FILTER} cityOptions={distanceData}
className={`filterIcon ${isSelect && "active"}`} quickOptions={quickFilterData}
onChange={handleDistanceOrQuickChange}
cityName="distance"
quickName="quick"
cityValue={distanceQuickFilter?.distance}
quickValue={distanceQuickFilter?.quick}
/> />
{isSelect && <Text className="filterCount">{filterCount}</Text>} {/* 筛选 icon */}
</View> <View
{/* 筛选弹框 */} className={`filterIconWrapper ${isSelect && "active"}`}
{/* 综合筛选 */} onClick={toggleShowPopup}
{isShowFilterPopup && ( >
<View> <Image
<FilterPopup src={isSelect ? img.ICON_FILTER_SELECTED : img.ICON_FILTER}
loading={loading} className={`filterIcon ${isSelect && "active"}`}
onCancel={toggleShowPopup}
onConfirm={toggleShowPopup}
onChange={handleUpdateFilterOptions}
filterOptions={filterOptions}
onClear={clearFilterOptions}
visible={isShowFilterPopup}
onClose={toggleShowPopup}
// statusNavbarHeigh={0}
statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight}
/> />
{isSelect && <Text className="filterCount">{filterCount}</Text>}
</View> </View>
)} {/* 综合筛选 */}
</View> {isShowFilterPopup && (
<View>
<FilterPopup
loading={loading}
onCancel={toggleShowPopup}
onConfirm={handleFilterConfirm}
onChange={handleUpdateFilterOptions}
filterOptions={filterOptions}
onClear={clearFilterOptions}
visible={isShowFilterPopup}
onClose={toggleShowPopup}
statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight}
/>
</View>
)}
</View>
{/* 列表内容 */} {/* 列表内容 */}
<ListContainer <ListContainer
data={searchResultData} data={searchResultData}
recommendList={recommendList} recommendList={recommendList}
loading={loading} loading={loading}
error={error} error={error}
reload={refreshMatches} reload={refreshMatches}
loadMoreMatches={loadMoreMatches} loadMoreMatches={loadMoreMatches}
/> />
</View> </View>
</>
); );
}; };

View File

@@ -8,7 +8,7 @@ interface GlobalState {
getLocationText: string; getLocationText: string;
statusNavbarHeightInfo: { statusNavbarHeightInfo: {
statusBarHeight: number; statusBarHeight: number;
navbarHeight: number; navBarHeight: number;
totalHeight: number; totalHeight: number;
}; };
} }
@@ -33,18 +33,18 @@ export const useGlobalStore = create<GlobalStore>()((set, get) => ({
// 状态栏和导航栏高度信息 // 状态栏和导航栏高度信息
statusNavbarHeightInfo: { statusNavbarHeightInfo: {
statusBarHeight: 0, statusBarHeight: 0,
navbarHeight: 0, navBarHeight: 0,
totalHeight: 0, totalHeight: 0,
}, },
// 获取导航栏高度信息 // 获取导航栏高度信息
getNavbarHeightInfo: () => { getNavbarHeightInfo: () => {
const { statusBarHeight, navbarHeight } = getNavbarHeight(); const { statusBarHeight, navBarHeight } = getNavbarHeight();
set({ set({
statusNavbarHeightInfo: { statusNavbarHeightInfo: {
statusBarHeight, statusBarHeight,
navbarHeight, navBarHeight,
totalHeight: statusBarHeight + navbarHeight, totalHeight: statusBarHeight + navBarHeight,
}, },
}); });
}, },

View File

@@ -40,32 +40,73 @@ const defaultPageOption = {
pageSize: 20, pageSize: 20,
}; };
// 创建 store // 页面状态默认值
const now = new Date(); const pageStateDefaultValue = {
export const useListStore = create<TennisStore>()((set, get) => ({ // 列表数据
currentPage: "", data: [],
isSearchResult: false,
searchResultData: [],
dateRange: [now, new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000)], // 日期区间
// 初始状态
matches: [],
// 推荐列表 // 推荐列表
recommendList: [], recommendList: [],
location: {
latitude: 0,
longitude: 0,
}, // 位置
// 是否加载中
loading: true,
error: null,
// 搜索的value
searchValue: "",
// 是否展示综合筛选弹窗 // 是否展示综合筛选弹窗
isShowFilterPopup: false, isShowFilterPopup: false,
// 综合筛选项 // 综合筛选项
filterOptions: defaultFilterOptions, filterOptions: defaultFilterOptions,
// 距离筛选和快捷筛选
distanceQuickFilter: defaultDistanceQuickFilter,
// 综合筛选 选择的筛选数量 // 综合筛选 选择的筛选数量
filterCount: 0, filterCount: 0,
// 分页
pageOption: defaultPageOption,
// 球局数量
gamesNum: 0,
// 是否还有更多数据
isHasMoreData: true,
}
// 列表页状态
const listPageStateDefaultValue = {
...pageStateDefaultValue,
// 列表页是否显示搜索框自定义导航
isShowInputCustomerNavBar: false,
}
// 搜索页状态
const searchPageStateDefaultValue = {
...pageStateDefaultValue,
// 搜索结果数据
searchResultData: [],
// 联想词
suggestionList: [],
// 是否显示联想词
isShowSuggestion: false,
// 搜索历史数据
searchHistory: [],
// 搜索历史数据默认 Top 15
searchHistoryParams: {
page: 1,
pageSize: 15,
},
}
// const now = new Date();
// 公共属性
const commonStateDefaultValue = {
// 是否是搜索结果页
isSearchResult: false,
// 是否加载中
loading: true,
// 错误信息
error: null,
// 位置
location: {
latitude: 0,
longitude: 0,
},
// 搜索的value
searchValue: "",
// 日期区间
// dateRange: [now, new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000)],
// 距离筛选数据 // 距离筛选数据
distanceData: [ distanceData: [
{ id: 0, label: "全城", value: "" }, { id: 0, label: "全城", value: "" },
@@ -79,8 +120,6 @@ export const useListStore = create<TennisStore>()((set, get) => ({
{ label: "距离更近", value: "1" }, { label: "距离更近", value: "1" },
{ label: "时间更近", value: "2" }, { label: "时间更近", value: "2" },
], ],
// 距离筛选和快捷筛选
distanceQuickFilter: defaultDistanceQuickFilter,
// 气泡日期范围 // 气泡日期范围
dateRangeOptions: [ dateRangeOptions: [
@@ -97,42 +136,28 @@ export const useListStore = create<TennisStore>()((set, get) => ({
{ id: 5, label: "晚上 18:00-22:00", value: "18:00-22: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" }, { id: 6, label: "夜间 22:00-24:00", value: "22:00-24:00" },
], ],
// 球局数量 }
// 创建 store
export const useListStore = create<TennisStore>()((set, get) => ({
currentPage: "",
// 列表页
listPageState: listPageStateDefaultValue,
// 搜索及搜索结果页
searchPageState: searchPageStateDefaultValue,
...commonStateDefaultValue,
gamesNum: 0, gamesNum: 0,
// 是否还有更多数据
isHasMoreData: true,
// 页面滚动距离顶部距离 是否大于0
isScrollTop: false,
// 搜索历史数据
searchHistory: [],
// 搜索历史数据默认 Top 15
searchHistoryParams: {
page: 1,
pageSize: 15,
},
// 联想词
suggestionList: [],
// 是否显示联想词
isShowSuggestion: false,
// 列表页是否显示搜索框自定义导航
isShowInputCustomerNavBar: false,
// 结果页是否显示搜索框自定义导航
isShowResultInputCustomerNavBar: false,
// 打开距离筛选框
isOpenDistancePopup: false,
// 打开快捷筛选框
isOpenQuickFilterPopup: false,
// 分页
pageOption: defaultPageOption,
// 组装搜索数据 // 组装搜索数据
getSearchParams: () => { getSearchParams: () => {
const state = get(); const state = get();
const filterOptions = state?.filterOptions || {}; const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const distanceQuickFilter = state?.distanceQuickFilter || {}; const filterOptions = currentPageState?.filterOptions || {};
const distanceQuickFilter = currentPageState?.distanceQuickFilter || {};
const params = { const params = {
pageOption: state.pageOption, pageOption: currentPageState?.pageOption,
seachOption: { seachOption: {
...filterOptions, ...filterOptions,
title: state.searchValue, title: state.searchValue,
@@ -147,30 +172,45 @@ export const useListStore = create<TennisStore>()((set, get) => ({
return params; return params;
}, },
// 设置搜索的列表结果 // 设置列表结果
setListData: (payload: IPayload & { isAppend?: boolean }) => { setListData: (payload: IPayload & { isAppend?: boolean }) => {
const { isSearchResult } = get(); const state = get();
const { error, data, loading, count, isAppend = false } = payload; const { error, data, loading, count, isAppend = false } = payload;
const saveKey = isSearchResult ? "searchResultData" : "matches";
const isHasMoreData = count > 0; const isHasMoreData = count > 0;
if (isAppend) { if (state.isSearchResult) {
// 追加数据到现有数组 // 更新搜索页状态
const currentData = get()[saveKey] || []; const currentData = state.searchPageState?.searchResultData || [];
const newData = [...currentData, ...(data || [])]; const newData = isAppend ? [...currentData, ...(data || [])] : (data || []);
const saveData = { error, loading, count, isHasMoreData, [saveKey]: newData }; set({
set({...saveData}); searchPageState: {
...state.searchPageState,
searchResultData: newData,
isHasMoreData,
},
error,
loading,
});
} else { } else {
// 替换整个数组 // 更新列表页状态
const saveData = { error, loading, count, isHasMoreData, [saveKey]: data }; const currentData = state.listPageState?.data || [];
set({...saveData}); const newData = isAppend ? [...currentData, ...(data || [])] : (data || []);
set({
listPageState: {
...state.listPageState,
data: newData,
isHasMoreData,
},
error,
loading,
});
} }
}, },
// 获取列表数据(常规搜索) // 获取列表数据(常规搜索)
fetchMatches: async (params, isFirstLoad = false, isAppend = false) => { fetchMatches: async (params, isFirstLoad = false, isAppend = false) => {
set({ loading: true, error: null }); set({ loading: true, error: null });
const { getSearchParams, setListData, distanceQuickFilter } = get(); const { getSearchParams, setListData } = get();
try { try {
const searchParams = getSearchParams() || {}; const searchParams = getSearchParams() || {};
@@ -179,6 +219,12 @@ export const useListStore = create<TennisStore>()((set, get) => ({
...params, ...params,
}; };
// 获取当前页面的距离筛选
const state = get();
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
console.log("===当前页面状态:", state.isSearchResult, currentPageState);
const distanceQuickFilter = currentPageState?.distanceQuickFilter || {};
// 是否选择了智能排序 // 是否选择了智能排序
const isIntegrate = distanceQuickFilter?.quick === "0"; const isIntegrate = distanceQuickFilter?.quick === "0";
let fetchFn = getGamesList; let fetchFn = getGamesList;
@@ -193,7 +239,7 @@ export const useListStore = create<TennisStore>()((set, get) => ({
} }
} }
console.log("===fetchMatches 获取列表数据参数:", reqParams); console.log("===获取列表数据参数:", reqParams);
const resData = (await fetchFn(reqParams)) || {}; const resData = (await fetchFn(reqParams)) || {};
const { data = {}, code } = resData; const { data = {}, code } = resData;
if (code !== 0) { if (code !== 0) {
@@ -245,11 +291,15 @@ export const useListStore = create<TennisStore>()((set, get) => ({
// 获取历史搜索数据 // 获取历史搜索数据
getSearchHistory: async () => { getSearchHistory: async () => {
try { try {
const params = get()?.searchHistoryParams || {}; const state = get();
const params = state.searchPageState?.searchHistoryParams || {};
const resData = (await getSearchHistory(params)) || {}; const resData = (await getSearchHistory(params)) || {};
const searchHistory = resData?.data?.records || []; const searchHistory = resData?.data?.records || [];
set({ set({
searchHistory, searchPageState: {
...state.searchPageState,
searchHistory,
},
}); });
} catch (error) { } } catch (error) { }
}, },
@@ -257,11 +307,15 @@ export const useListStore = create<TennisStore>()((set, get) => ({
// 清空历史记录 // 清空历史记录
clearHistory: async () => { clearHistory: async () => {
try { try {
const state = get();
const params = {}; const params = {};
const resData = (await clearHistory(params)) || {}; const resData = (await clearHistory(params)) || {};
if (resData?.code === 0) { if (resData?.code === 0) {
set({ set({
searchHistory: [], searchPageState: {
...state.searchPageState,
searchHistory: [],
},
}); });
} }
} catch (error) { } } catch (error) { }
@@ -270,17 +324,25 @@ export const useListStore = create<TennisStore>()((set, get) => ({
// 获取联想 // 获取联想
searchSuggestion: async (val: string) => { searchSuggestion: async (val: string) => {
try { try {
const state = get();
const resData = (await searchSuggestion({ val, limit: 10 })) || {}; const resData = (await searchSuggestion({ val, limit: 10 })) || {};
const recommendations = resData?.data?.recommendations || []; const recommendations = resData?.data?.recommendations || [];
const total = resData?.data?.total; const total = resData?.data?.total;
set({ set({
suggestionList: recommendations, searchPageState: {
isShowSuggestion: total > 0, ...state.searchPageState,
suggestionList: recommendations,
isShowSuggestion: total > 0,
},
}); });
} catch (error) { } catch (error) {
const state = get();
set({ set({
suggestionList: [], searchPageState: {
isShowSuggestion: true, ...state.searchPageState,
suggestionList: [],
isShowSuggestion: true,
},
}); });
} }
}, },
@@ -292,67 +354,154 @@ export const useListStore = create<TennisStore>()((set, get) => ({
// 更新综合筛选项 // 更新综合筛选项
updateFilterOptions: (payload: Record<string, any>) => { updateFilterOptions: (payload: Record<string, any>) => {
const { filterOptions: preFilterOptions, fetchGetGamesCount } = get() || {}; const state = get();
const filterOptions = { ...preFilterOptions, ...payload }; const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const filterOptions = { ...currentPageState?.filterOptions, ...payload };
const filterCount = Object.values(filterOptions).filter(Boolean).length; const filterCount = Object.values(filterOptions).filter(Boolean).length;
console.log("===更新综合筛选项", filterOptions, filterCount);
set({ if (state.isSearchResult) {
filterOptions, set({
filterCount, searchPageState: {
pageOption: defaultPageOption, ...state.searchPageState,
}); filterOptions,
filterCount,
pageOption: defaultPageOption,
},
});
} else {
set({
listPageState: {
...state.listPageState,
filterOptions,
filterCount,
pageOption: defaultPageOption,
},
});
}
// 获取球局数量 // 获取球局数量
fetchGetGamesCount(); state.fetchGetGamesCount();
}, },
// 清空综合筛选选项 // 清空综合筛选选项
clearFilterOptions: () => { clearFilterOptions: () => {
const { getMatchesData, fetchGetGamesCount } = get() || {}; const state = get();
set({ const { getMatchesData, fetchGetGamesCount } = state;
filterOptions: defaultFilterOptions,
filterCount: 0, if (state.isSearchResult) {
pageOption: defaultPageOption, set({
}); searchPageState: {
...state.searchPageState,
filterOptions: defaultFilterOptions,
filterCount: 0,
pageOption: defaultPageOption,
},
});
} else {
set({
listPageState: {
...state.listPageState,
filterOptions: defaultFilterOptions,
filterCount: 0,
pageOption: defaultPageOption,
},
});
}
getMatchesData(); getMatchesData();
fetchGetGamesCount(); fetchGetGamesCount();
}, },
// 加载更多数据 // 加载更多数据
loadMoreMatches: () => { loadMoreMatches: () => {
const { pageOption, fetchMatches, isHasMoreData } = get() || {}; const state = get();
const currentPageState = state.isSearchResult ? state.searchPageState : state.listPageState;
const { pageOption, isHasMoreData } = currentPageState || {};
if (!isHasMoreData) { if (!isHasMoreData) {
return; return;
} }
set({
pageOption: { const newPageOption = {
page: pageOption?.page + 1, page: (pageOption?.page || 1) + 1,
pageSize: 20, pageSize: 20,
}, };
});
if (state.isSearchResult) {
set({
searchPageState: {
...state.searchPageState,
pageOption: newPageOption,
},
});
} else {
set({
listPageState: {
...state.listPageState,
pageOption: newPageOption,
},
});
}
// 加载更多时追加数据到现有数组 // 加载更多时追加数据到现有数组
fetchMatches({}, false, true); state.fetchMatches({}, false, true);
}, },
// 初始化搜索条件 重新搜索 // 初始化搜索条件 重新搜索
initialFilterSearch: async () => { initialFilterSearch: async () => {
const { getMatchesData, fetchGetGamesCount } = get(); const state = get();
set({ const { getMatchesData, fetchGetGamesCount } = state;
distanceQuickFilter: defaultDistanceQuickFilter,
filterOptions: defaultFilterOptions, if (state.isSearchResult) {
pageOption: defaultPageOption, set({
}); searchPageState: {
...state.searchPageState,
distanceQuickFilter: defaultDistanceQuickFilter,
filterOptions: defaultFilterOptions,
pageOption: defaultPageOption,
},
});
} else {
set({
listPageState: {
...state.listPageState,
distanceQuickFilter: defaultDistanceQuickFilter,
filterOptions: defaultFilterOptions,
pageOption: defaultPageOption,
},
});
}
fetchGetGamesCount(); fetchGetGamesCount();
return await getMatchesData(); return await getMatchesData();
}, },
// 更新store数据 // 更新store数据
updateState: (payload: Record<string, any>) => { updateState: (payload: Record<string, any>) => {
const state = get();
console.log("Store: 更新数据:", state);
set({ set({
...(payload || {}), ...(payload || {}),
}); });
}, },
// 更新列表页状态中的特定字段
updateListPageState: (payload: Record<string, any>) => {
const state = get();
set({
listPageState: {
...state.listPageState,
...payload,
},
});
console.log("===更新列表页状态:", state);
},
// 更新搜索页状态中的特定字段
updateSearchPageState: (payload: Record<string, any>) => {
const state = get();
set({
searchPageState: {
...state.searchPageState,
...payload,
},
});
console.log("===更新搜索页状态:", state);
},
})); }));
// 导出便捷的 hooks // 导出便捷的 hooks

View File

@@ -1,13 +1,39 @@
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
export const getNavbarHeight = (): { statusBarHeight: number; navbarHeight: number; totalHeight: number; } => { export const getNavbarHeight = (): { statusBarHeight: number; navBarHeight: number; totalHeight: number; } => {
// 1. 获取系统信息,包含状态栏高度
const systemInfo = Taro.getSystemInfoSync(); const systemInfo = Taro.getSystemInfoSync();
const statusBarHeight = systemInfo?.statusBarHeight || 0; const statusBarHeight = systemInfo.statusBarHeight || 0;
const isIOS = systemInfo.platform === "ios"; const isIOS = systemInfo.platform === "ios";
const navbarHeight = isIOS ? 44 : 48; const isIPad = systemInfo.model?.toLowerCase().includes('ipad') ||
(systemInfo.platform === 'ios' && systemInfo.screenWidth >= 768);
// 2. 获取胶囊按钮位置信息(用于计算导航栏高度)
const menuButtonInfo = Taro.getMenuButtonBoundingClientRect();
let navBarHeight = 44 // 默认导航栏高度
if (menuButtonInfo && menuButtonInfo.top) {
// 计算导航栏高度公式
navBarHeight = (menuButtonInfo.top - statusBarHeight) * 2 + menuButtonInfo.height
} else {
// 胶囊按钮信息不可用时,使用默认值
if (isIPad) {
navBarHeight = 50; // iPad 上的导航栏高度通常更高
} else if (isIOS) {
navBarHeight = 44; // iPhone 导航栏高度
} else {
navBarHeight = 48; // Android 导航栏高度
}
}
const totalHeight = statusBarHeight + navBarHeight;
return { return {
statusBarHeight, statusBarHeight,
navbarHeight, navBarHeight,
totalHeight: statusBarHeight + navbarHeight, totalHeight,
}; };
}; };

View File

@@ -20,12 +20,45 @@ export interface IFilterOptions {
playType?: string, // 玩法 playType?: string, // 玩法
distanceFilter?: string distanceFilter?: string
} }
// 页面状态接口
export interface PageState {
data: TennisMatch[]
recommendList: TennisMatch[]
isShowFilterPopup: boolean
filterOptions: IFilterOptions
distanceQuickFilter: {
distance: string
quick: string
}
filterCount: number
pageOption: {
page: number
pageSize: number
}
gamesNum: number
isHasMoreData: boolean
}
// 列表页状态
export interface ListPageState extends PageState {
isShowInputCustomerNavBar: boolean
}
// 搜索页状态
export interface SearchPageState extends PageState {
searchResultData: TennisMatch[]
suggestionList: string[]
isShowSuggestion: boolean
searchHistory: {id: number, title: string}[]
searchHistoryParams: Record<string, any>
}
// 主状态接口
export interface ListState { export interface ListState {
currentPage: string currentPage: string
isSearchResult: boolean isSearchResult: boolean
searchResultData: TennisMatch[] listPageState: ListPageState
matches: TennisMatch[] searchPageState: SearchPageState
recommendList: TennisMatch[]
location: { location: {
latitude: number latitude: number
longitude: number longitude: number
@@ -33,32 +66,11 @@ export interface ListState {
loading: boolean loading: boolean
error: string | null error: string | null
searchValue: string searchValue: string
isShowFilterPopup: boolean
filterOptions: IFilterOptions
filterCount: number
distanceData: any[] distanceData: any[]
quickFilterData: any[] quickFilterData: any[]
distanceQuickFilter: {
distance: string
quick: string
}
timeBubbleData: BubbleOption[] timeBubbleData: BubbleOption[]
dateRangeOptions: BubbleOption[] dateRangeOptions: BubbleOption[]
gamesNum: number gamesNum: number
isHasMoreData: boolean
isScrollTop: boolean
searchHistoryParams: Record<string, any>
searchHistory: {id: number, title: string}[]
suggestionList: string[]
isShowSuggestion: boolean
isShowInputCustomerNavBar: boolean
isShowResultInputCustomerNavBar: boolean
isOpenDistancePopup: boolean,
isOpenQuickFilterPopup: boolean,
pageOption: {
page: number
pageSize: number
}
} }
export interface ListActions { export interface ListActions {
@@ -67,6 +79,8 @@ export interface ListActions {
getMatchesData: () => void getMatchesData: () => void
clearError: () => void clearError: () => void
updateState: (payload: Record<string, any>) => void updateState: (payload: Record<string, any>) => void
updateListPageState: (payload: Record<string, any>) => void
updateSearchPageState: (payload: Record<string, any>) => void
updateFilterOptions: (payload: Record<string, any>) => void updateFilterOptions: (payload: Record<string, any>) => void
clearFilterOptions: () => void clearFilterOptions: () => void
getSearchHistory: () => Promise<void> getSearchHistory: () => Promise<void>