列表搜索页面

This commit is contained in:
juguohong
2025-09-06 22:08:34 +08:00
parent c78be00ded
commit 2d0d728969
15 changed files with 518 additions and 128 deletions

View File

@@ -1,146 +0,0 @@
import { Popup } from "@nutui/nutui-react-taro";
import Range from "../../components/Range";
import Bubble from "../../components/Bubble";
import styles from "./filterPopup.module.scss";
import TitleComponent from "@/components/Title";
import { Button } from "@nutui/nutui-react-taro";
import { Image } from "@tarojs/components";
import img from "../../config/images";
import { useListStore } from "src/store/listStore";
import { FilterPopupProps } from "../../../types/list/types";
// 场地
import CourtType from "@/components/CourtType";
// 玩法
import GamePlayType from "@/components/GamePlayType";
import { useDictionaryActions } from "@/store/dictionaryStore";
import { useMemo } from "react";
const FilterPopup = (props: FilterPopupProps) => {
const {
loading,
onCancel,
onConfirm,
onChange,
filterOptions,
onClear,
visible,
onClose,
statusNavbarHeigh,
} = props;
const store = useListStore() || {};
const { getDictionaryValue } = useDictionaryActions() || {};
const { timeBubbleData } = store;
const handleOptions = (dictionaryValue: []) => {
return dictionaryValue?.map((item) => ({ label: item, value: item })) || [];
};
const courtType = getDictionaryValue("court_type") || [];
const locationOptions = useMemo(() => {
return courtType ? handleOptions(courtType) : [];
}, [courtType]);
const gamePlay = getDictionaryValue("game_play") || [];
const gamePlayOptions = useMemo(() => {
return gamePlay ? handleOptions(gamePlay) : [];
}, [gamePlay]);
const handleFilterChange = (name, value) => {
onChange({ [name]: value });
};
const handleClearFilter = () => {
onClear();
onCancel();
};
return (
<>
<Popup
destroyOnClose
position="top"
round
visible={visible}
onClose={onClose}
style={{ marginTop: statusNavbarHeigh + "px" }}
overlayStyle={{ marginTop: statusNavbarHeigh + "px" }}
>
<div className={styles.filterPopupWrapper}>
{/* 时间气泡选项 */}
<Bubble
options={timeBubbleData}
value={filterOptions?.time}
onChange={handleFilterChange}
layout="grid"
size="small"
columns={3}
name="time"
/>
{/* 范围选择 */}
<Range
min={1.0}
max={5.0}
step={0.5}
className={styles.filterPopupRange}
onChange={handleFilterChange}
value={filterOptions?.ntrp}
name="ntrp"
/>
{/* 场次气泡选项 */}
{/* <div>
<TitleComponent
title="场地类型"
icon={<Image src={img.ICON_SITE} />}
/>
<Bubble
options={locationOptions}
value={filterOptions?.site}
onChange={handleFilterChange}
layout="grid"
size="small"
columns={3}
name="site"
/>
</div> */}
{/* CourtType */}
<CourtType
onChange={handleFilterChange}
name="court_type"
options={locationOptions}
value={filterOptions?.site}
/>
{/* 玩法 */}
<GamePlayType
onChange={handleFilterChange}
name="game_play"
options={gamePlayOptions}
value={filterOptions?.game_play}
/>
{/* 按钮 */}
<div className={styles.filterPopupBtnWrapper}>
<Button
className={styles.btn}
type="default"
onClick={handleClearFilter}
>
</Button>
<Button
className={`${styles.btn} ${styles.confirm}`}
type="default"
loading={loading}
onClick={onConfirm}
>
332
</Button>
</div>
</div>
</Popup>
</>
);
};
export default FilterPopup;

View File

@@ -1,35 +0,0 @@
.filterPopupWrapper {
position: relative;
$m18: 18px;
padding: $m18;
.filterPopupRange {
margin-top: $m18;
margin-bottom: $m18;
}
.filterPopupBtnWrapper {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
position: sticky;
bottom: 0;
background-color: #ffffff;
padding: 8px 0;
.btn {
flex: 1;
}
--nutui-button-border-width: 0.5px;
--nutui-button-default-border-color: #0000000F;
--nutui-button-border-radius: 16px;
--nutui-button-default-height: 44px;
--nutui-button-default-color: #000000;
.confirm {
--nutui-button-default-color: #ffffff;
--nutui-button-default-background-color: #000000;
}
}
}

View File

@@ -1,10 +1,8 @@
import Menu from "../../components/Menu";
import CityFilter from "../../components/CityFilter";
import SearchBar from "../../components/SearchBar";
import FilterPopup from "./FilterPopup";
import FilterPopup from "@/components/FilterPopup";
import styles from "./index.module.scss";
import { useEffect } from "react";
import Taro, { usePageScroll, useReachBottom } from "@tarojs/taro";
import Taro, { usePageScroll } from "@tarojs/taro";
import { useListStore } from "@/store/listStore";
import { useGlobalState } from "@/store/global";
import { View } from "@tarojs/components";
@@ -19,7 +17,7 @@ const ListPage = () => {
// 从 store 获取数据和方法
const store = useListStore() || {};
const { statusNavbarHeightInfo } = useGlobalState() || {};
const { statusNavbarHeightInfo, location } = useGlobalState() || {};
const { totalHeight } = statusNavbarHeightInfo || {};
const {
isShowFilterPopup,
@@ -43,25 +41,18 @@ const ListPage = () => {
} = store;
usePageScroll((res) => {
// if (res?.scrollTop > 0 && !isScrollTop) {
// updateState({ isScrollTop: true });
// }
if (res?.scrollTop >= totalHeight && !isScrollTop) {
updateState({ isShowInputCustomerNavBar: true });
!isShowInputCustomerNavBar && updateState({ isShowInputCustomerNavBar: true });
} else {
updateState({ isShowInputCustomerNavBar: false });
isShowInputCustomerNavBar && updateState({ isShowInputCustomerNavBar: false });
}
});
useReachBottom(() => {
console.log("触底了");
// 调用 store 的加载更多方法
// loadMoreMatches();
});
useEffect(() => {
// 页面加载时获取数据
fetchMatches();
// 保存位置
updateState({ location });
}, []);
// 下拉刷新处理函数 - 使用Taro生命周期钩子

View File

@@ -5,11 +5,60 @@
display: flex;
align-items: center;
gap: 5px;
padding: 5px 0px 10px 0px;
padding: 5px 15px 10px 15px;
position: sticky;
top: -1px;
background-color: #fefefe;
z-index: 123;
.nut-menu-bar {
padding: 0;
}
.nut-menu-container-wrap {
left: -15px;
}
.filterIconWrapper {
display: flex;
width: 28px;
height: 28px;
justify-content: center;
align-items: center;
gap: 10px;
flex-shrink: 0;
aspect-ratio: 1/1;
border-radius: 999px;
border: 0.5px solid rgba(0, 0, 0, 0.06);
background: #FFF;
position: relative;
&.active {
background-color: #000000;
}
}
.filterIcon {
width: 14px;
height: 14px;
flex-shrink: 0;
}
.filterCount {
background-color: #000000;
position: absolute;
width: 15px;
height: 15px;
border: 2px solid #ffffff;
border-radius: 50%;
right: -5px;
bottom: -5px;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
}
}
.menuFilter {

View File

@@ -1,34 +1,53 @@
import { View } from "@tarojs/components";
import { useListState } from "@/store/listStore";
import { View, Image, Text } from "@tarojs/components";
import { useSearchResultState } from "@/store/searchResultStore";
import { useGlobalState } from "@/store/global";
import ListContainer from "@/container/listContainer";
import img from "@/config/images";
import "./index.scss";
import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { useEffect } from "react";
import FilterPopup from "@/components/FilterPopup";
const SearchResult = () => {
const {
distanceData,
quickFilterData,
isShowFilterPopup,
error,
distanceQuickFilter,
updateState,
matches,
recommendList,
loading,
error,
refreshMatches,
fetchMatches,
} = useListState() || {};
refreshMatches,
updateState,
filterCount,
updateFilterOptions, // 更新筛选条件
filterOptions,
clearFilterOptions,
distanceData,
quickFilterData,
} = useSearchResultState() || {};
const { statusNavbarHeightInfo } = useGlobalState() || {};
const { totalHeight } = statusNavbarHeightInfo || {}
const { totalHeight } = statusNavbarHeightInfo || {};
const isSelect = filterCount > 0;
useEffect(() => {
// 页面加载时获取数据
fetchMatches();
}, []);
/**
* @description 更新筛选条件
* @param {Record<string, any>} params 筛选项
*/
const handleUpdateFilterOptions = (params: Record<string, any>) => {
updateFilterOptions(params);
};
const toggleShowPopup = () => {
updateState({ isShowFilterPopup: !isShowFilterPopup });
};
// 距离筛选
const handleDistanceOrQuickChange = (name, value) => {
updateState({
@@ -42,10 +61,15 @@ const SearchResult = () => {
return (
<View className="searchResultPage">
{/* 筛选 */}
<View className='searchResultFilterWrapper' style={{
// top: `${totalHeight}px`
}}>
<DistanceQuickFilter
<View
className="searchResultFilterWrapper"
style={
{
// top: `${totalHeight}px`
}
}
>
<DistanceQuickFilter
cityOptions={distanceData}
quickOptions={quickFilterData}
onChange={handleDistanceOrQuickChange}
@@ -54,6 +78,31 @@ const SearchResult = () => {
cityValue={distanceQuickFilter?.distance}
quickValue={distanceQuickFilter?.quick}
/>
{/* 筛选 icon */}
<View className={`filterIconWrapper ${isSelect && 'active'}`} onClick={toggleShowPopup}>
<Image
src={isSelect ? img.ICON_FILTER_SELECTED : img.ICON_FILTER}
className={`filterIcon ${isSelect && 'active'}`}
/>
{isSelect && <Text className="filterCount">{filterCount}</Text>}
</View>
{/* 筛选弹框 */}
{/* 综合筛选 */}
{isShowFilterPopup && (
<View>
<FilterPopup
loading={loading}
onCancel={toggleShowPopup}
onConfirm={toggleShowPopup}
onChange={handleUpdateFilterOptions}
filterOptions={filterOptions}
onClear={clearFilterOptions}
visible={isShowFilterPopup}
onClose={toggleShowPopup}
statusNavbarHeigh={0}
/>
</View>
)}
</View>
{/* 列表内容 */}
@@ -68,4 +117,4 @@ const SearchResult = () => {
);
};
export default SearchResult;
export default SearchResult;