列表搜索页面

This commit is contained in:
juguohong
2025-09-01 00:03:43 +08:00
parent 7d4eb3d031
commit fdd6b8f7cb
18 changed files with 315 additions and 83 deletions

View File

@@ -1,6 +1,6 @@
export default defineAppConfig({
pages: [
'pages/publishBall/index',
// 'pages/publishBall/index',
'pages/list/index', // 列表页
'pages/search/index', // 搜索页
'pages/searchResult/index', // 搜索结果页面

View File

@@ -9,7 +9,7 @@ import {DistanceFilterProps} from '../../../types/list/types'
const MenuComponent = (props: DistanceFilterProps) => {
const { value, onChange, wrapperClassName, itemClassName, options, name } =
const { value, onChange, wrapperClassName, itemClassName, options, name, onOpen, onClose } =
props;
const [isChange, setIsChange] = useState(false);
const [iOpen, setIsOpen] = useState(false);
@@ -23,10 +23,12 @@ const MenuComponent = (props: DistanceFilterProps) => {
const handleOpen = () => {
setIsOpen(true);
onOpen && typeof onOpen === "function" && onOpen();
};
const handleClose = () => {
setIsOpen(false);
onClose && typeof onClose === "function" && onClose();
};
return (

View File

@@ -0,0 +1,91 @@
.distanceQuickFilterWrap {
width: 100%;
.nut-menu-bar {
background-color: unset;
box-shadow: unset;
padding: 0 15px;
gap: 5px;
}
.nut-menu-title {
flex: unset;
box-sizing: border-box;
display: flex;
height: 28px;
padding: 4px 10px;
justify-content: center;
align-items: center;
gap: 2px;
border-radius: 999px;
border: 0.5px solid rgba(0, 0, 0, 0.06);
background: #ffffff;
font-size: 14px;
font-weight: 600;
line-height: 20px;
}
.nut-menu-title.active {
color: #000;
}
.nut-menu-container-wrap {
width: 100vw;
border-bottom-left-radius: 30px;
border-bottom-right-radius: 30px;
}
.nut-menu-container-item {
color: rgba(60, 60, 67, 0.60);
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;
justify-content: space-between;
width: 100%;
margin-bottom: 16px;
}
.title {
font-size: 14px;
font-weight: 600;
}
.cityName {
font-size: 13px;
font-weight: 400;
color: #3c3c43;
}
.distanceWrap {
margin-bottom: 16px;
width: 100%;
}
.distanceBubbleItem {
display: flex;
width: 80px;
height: 28px;
padding: 4px 10px;
justify-content: center;
align-items: center;
gap: 2px;
border-radius: 999px;
border: 0.5px solid rgba(0, 0, 0, 0.06);
}
.itemIcon {
width: 20px;
height: 20px;
}
}

View File

@@ -0,0 +1,81 @@
import React, { useRef, useState } from "react";
import { Menu, Button } from "@nutui/nutui-react-taro";
import { Image } from "@tarojs/components";
import img from "@/config/images";
import Bubble from "../Bubble";
import "./index.scss";
const Demo3 = (props) => {
const {
cityOptions,
quickOptions,
onChange,
cityName,
quickName,
cityValue,
quickValue,
} = props;
const itemRef = useRef(null);
const handleChange = (name: string, value: string) => {
// setIsChange(true);
onChange && onChange(name, value);
(itemRef.current as any)?.toggle(false);
};
return (
<Menu
className="distanceQuickFilterWrap"
>
<Menu.Item
title={cityValue}
ref={itemRef}
icon={<Image src={img.ICON_MENU_ITEM_SELECTED} />}
>
<div className="positionWrap">
<p className="title"></p>
<p className="cityName"></p>
</div>
<div className="distanceWrap">
<Bubble
options={cityOptions}
value={cityValue}
onChange={handleChange}
layout="grid"
size="small"
columns={4}
itemClassName="distanceBubbleItem"
name={cityName}
/>
</div>
</Menu.Item>
<Menu.Item
options={quickOptions}
defaultValue={quickValue}
onChange={(value) => handleChange(quickName, value)}
icon={<Image className="itemIcon" src={img.ICON_MENU_ITEM_SELECTED} />}
/>
{/* <Menu.Item title="筛选" ref={itemRef}>
<div
style={{
width: '50%',
lineHeight: '28px',
padding: '0 30px',
}}
>
自定义内容
</div>
<Button
size="small"
onClick={() => {
;(itemRef.current as any)?.toggle(false)
}}
>
确认
</Button>
</Menu.Item> */}
</Menu>
);
};
export default Demo3;

View File

@@ -6,7 +6,7 @@ import { MenuFilterProps } from "../../../types/list/types";
import styles from "./index.module.scss";
const MenuComponent = (props: MenuFilterProps) => {
const { options, value, onChange, wrapperClassName, itemClassName, name } =
const { options, value, onChange, wrapperClassName, itemClassName, name, onOpen, onClose,open } =
props;
const [isChange, setIsChange] = useState(false);
const [isOpen, setIsOpen] = useState(false);
@@ -18,10 +18,12 @@ const MenuComponent = (props: MenuFilterProps) => {
const handleOpen = () => {
setIsOpen(true);
onOpen && typeof onOpen === "function" && onOpen();
};
const handleClose = () => {
setIsOpen(false);
onClose && typeof onClose === "function" && onClose();
};
return (

View File

@@ -5,6 +5,7 @@
--nutui-searchbar-input-text-color: #000000;
--nutui-searchbar-input-padding: 0 0 0 10px;
--nutui-searchbar-padding: 10px 0 0 0;
background-color: unset;
:global(.nut-searchbar-content) {
box-shadow: 0 4px 48px #00000014;

View File

@@ -7,6 +7,7 @@ import { useGlobalState } from "@/store/global";
import { useListState } from "@/store/listStore";
import CustomNavbar from "@/components/CustomNavbar";
import { Input } from "@nutui/nutui-react-taro";
import Taro from "@tarojs/taro";
interface IProps {
icon: string;
@@ -34,6 +35,12 @@ const ListHeader = (props: IProps) => {
getCurrentLocal();
}, []);
const handleInputClick = () => {
Taro.navigateTo({
url: "/pages/search/index",
});
}
return (
<CustomNavbar>
<View
@@ -53,10 +60,10 @@ const ListHeader = (props: IProps) => {
src={img.ICON_LIST_SEARCH_SEARCH}
/>
<Input
// className="inputSearch"
placeholder="搜索上海的球局和场地"
clearable={false}
value={searchValue}
onClick={handleInputClick}
/>
</View>
</View>

View File

@@ -4,5 +4,19 @@
display: flex;
flex-direction: column;
gap: 5px;
background-color: red;
// background-color: red;
.recommendTextWrapper {
display: flex;
align-items: center;
justify-content: center;
padding: 22px 10px;
}
.recommendText {
color: rgba(0, 0, 0, 0.85);
font-size: 14px;
font-weight: 500;
line-height: 24px;
}
}

View File

@@ -1,13 +1,18 @@
import { View } from "@tarojs/components";
import { View, Text } from "@tarojs/components";
import ListCard from "@/components/ListCard";
import ListLoadError from "@/components/ListLoadError";
import ListCardSkeleton from "@/components/ListCardSkeleton";
import "./index.scss";
const ListContainer = (props) => {
const { loading, data = [], error, reload } = props;
const { loading, data = [], error, reload, recommendList } = props;
const renderList = () => {
if (loading && data.length > 0) {
if (error) {
return <ListLoadError reload={reload} />;
}
const renderList = (list) => {
if (loading && list.length === 0) {
return (
<>
{new Array(10).fill(0).map(() => {
@@ -17,17 +22,9 @@ const ListContainer = (props) => {
);
}
if (error) {
return <ListLoadError reload={reload} />;
}
if (loading && data.length === 0) {
return null;
}
return (
<>
{data.map((match, index) => (
{list?.map((match, index) => (
<ListCard key={match.id || index} {...match} />
))}
</>
@@ -35,13 +32,12 @@ const ListContainer = (props) => {
};
return (
<View
className="listContentWrapper"
// style={{
// minHeight: `calc(100vh - ${totalHeight}px)`,
// }}
>
{renderList()}
<View className="listContentWrapper">
{renderList(data)}
<View className="recommendTextWrapper">
<Text className="recommendText"></Text>
</View>
{renderList(recommendList)}
</View>
);
};

View File

@@ -1,10 +1,10 @@
.listPage {
background-color: #fafafa;
background-color: #fefefe;
.listTopSearchWrapper {
padding: 0 15px;
// position: sticky;
background: #fefefe;
// background: #fefefe;
// z-index: 999;
}

View File

@@ -12,6 +12,7 @@ import CustomerNavBar from "@/container/listCustomNavbar";
import InputCustomerBar from "@/container/inputCustomerNavbar";
import GuideBar from "@/components/GuideBar";
import ListContainer from "@/container/listContainer";
import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import img from "@/config/images";
const ListPage = () => {
@@ -24,6 +25,7 @@ const ListPage = () => {
isShowFilterPopup,
error,
matches,
recommendList,
loading,
fetchMatches,
refreshMatches,
@@ -119,7 +121,7 @@ const ListPage = () => {
Taro.navigateTo({
url: "/pages/search/index",
});
}
};
return (
<>
@@ -162,30 +164,24 @@ const ListPage = () => {
/>
</View>
)}
{/* 筛选 */}
<div className={styles.listTopFilterWrapper}>
{/* 全城筛选 */}
<CityFilter
options={distanceData}
value={distanceQuickFilter?.distance}
wrapperClassName={styles.menuFilter}
onChange={handleDistanceOrQuickChange}
name="distance"
/>
{/* 智能排序 */}
<Menu
options={quickFilterData}
value={distanceQuickFilter?.quick}
onChange={handleDistanceOrQuickChange}
wrapperClassName={styles.menuFilter}
name="quick"
/>
</div>
</View>
{/* 筛选 */}
<View className={styles.listTopFilterWrapper}>
<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}

View File

@@ -1,4 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '',
navigationStyle: 'custom',
navigationBarTitleText: ''
})

View File

@@ -1,4 +1,3 @@
import InputCustomerBar from "@/container/inputCustomerNavbar";
import CustomerNavbarBack from "@/components/CustomerNavbarBack";
import { View, Image, Text } from "@tarojs/components";
import { Input } from "@nutui/nutui-react-taro";
@@ -18,7 +17,6 @@ const ListSearch = () => {
searchSuggestion,
suggestionList,
isShowSuggestion,
isShowInputCustomerNavBar,
} = useListState() || {};
const ref = useRef<any>(null);
@@ -45,6 +43,10 @@ const ListSearch = () => {
updateState({ searchValue: value });
if (value) {
searchSuggestion(value);
} else {
updateState({
isShowSuggestion: false,
});
}
};
@@ -85,11 +87,11 @@ const ListSearch = () => {
/**
* @description 点击搜索
*/
const handleSearch = () => {
const handleSearch = () => {
Taro.navigateTo({
url: `/pages/searchResult/index`,
});
}
};
// 是否显示清空图标
const isShowClearIcon = searchValue && searchValue?.length > 0;
@@ -100,11 +102,6 @@ const ListSearch = () => {
return (
<>
{!isShowInputCustomerNavBar ? (
<CustomerNavbarBack />
) : (
<InputCustomerBar icon={img.ICON_LIST_INPUT_LOGO} />
)}
<View className="listSearchContainer">
{/* 搜索 */}
<View className="topSearch">
@@ -128,7 +125,9 @@ const ListSearch = () => {
/>
)}
<View className="searchLine" />
<Text className="searchText" onClick={handleSearch}></Text>
<Text className="searchText" onClick={handleSearch}>
</Text>
</View>
</View>
{/* 联想词 */}

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '搜索结果',
// navigationStyle: 'custom',
})

View File

@@ -1,3 +1,18 @@
.menuFilter {
color: red;
.searchResultPage {
position: relative;
.searchResultFilterWrapper {
display: flex;
align-items: center;
gap: 5px;
padding: 5px 0px 10px 0px;
position: sticky;
top: -1px;
background-color: #fefefe;
z-index: 123;
}
.menuFilter {
padding: 0;
}
}

View File

@@ -1,11 +1,11 @@
import { View } from "@tarojs/components";
// import styles from "./index.scss";
import CityFilter from "@/components/CityFilter";
import Menu from "@/components/Menu";
import { useListState } from "@/store/listStore";
import { useGlobalState } from "@/store/global";
import ListContainer from "@/container/listContainer";
import "./index.scss";
import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { useEffect } from "react";
const SearchResult = () => {
const {
@@ -14,11 +14,21 @@ const SearchResult = () => {
distanceQuickFilter,
updateState,
matches,
recommendList,
loading,
error,
refreshMatches,
fetchMatches,
} = useListState() || {};
const { statusNavbarHeightInfo } = useGlobalState() || {};
const { totalHeight } = statusNavbarHeightInfo || {}
useEffect(() => {
// 页面加载时获取数据
fetchMatches();
}, []);
// 距离筛选
const handleDistanceOrQuickChange = (name, value) => {
updateState({
@@ -30,30 +40,26 @@ const SearchResult = () => {
};
return (
<View>
<View className="searchResultPage">
{/* 筛选 */}
<View className='searchResultFilterWrapper'>
{/* 全城筛选 */}
<CityFilter
options={distanceData}
value={distanceQuickFilter?.distance}
wrapperClassName='menuFilter'
onChange={handleDistanceOrQuickChange}
name="distance"
/>
{/* 智能排序 */}
<Menu
options={quickFilterData}
value={distanceQuickFilter?.quick}
onChange={handleDistanceOrQuickChange}
wrapperClassName='menuFilter'
name="quick"
/>
<View className='searchResultFilterWrapper' style={{
// top: `${totalHeight}px`
}}>
<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}

View File

@@ -19,6 +19,9 @@ const defaultDistance = 'all'; // 默认距离
export const useListStore = create<TennisStore>()((set, get) => ({
// 初始状态
matches: [],
// 推荐列表
recommendList: [],
// 是否加载中
loading: false,
error: null,
// 搜索的value
@@ -89,8 +92,14 @@ export const useListStore = create<TennisStore>()((set, get) => ({
suggestionList: [],
// 是否显示联想词
isShowSuggestion: false,
// 是否显示搜索框自定义导航
// 列表页是否显示搜索框自定义导航
isShowInputCustomerNavBar: false,
// 结果页是否显示搜索框自定义导航
isShowResultInputCustomerNavBar: false,
// 打开距离筛选框
isOpenDistancePopup: false,
// 打开快捷筛选框
isOpenQuickFilterPopup: false,
// 获取比赛数据
fetchMatches: async (params) => {
@@ -125,6 +134,8 @@ export const useListStore = create<TennisStore>()((set, get) => ({
})
set({
matches: list || rows || [],
recommendList: list || rows || [],
error: null,
loading: false,
gamesNum: count,
})