列表
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
export default defineAppConfig({
|
||||
pages: [
|
||||
'pages/list/index',
|
||||
'pages/list/index', // 列表页
|
||||
'pages/search/index', // 搜索页
|
||||
'pages/searchResult/index', // 搜索结果页面
|
||||
// 'pages/userInfo/myself/index',
|
||||
'pages/login/index/index',
|
||||
'pages/login/verification/index',
|
||||
|
||||
@@ -3,48 +3,4 @@
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
background-color: #ffffff;
|
||||
|
||||
.container {
|
||||
padding-left: 17px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 1px;
|
||||
height: 25px;
|
||||
background-color: #0000000F;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.change {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.cityWrapper {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.city {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.infoWrapper {
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
color: #3C3C4399;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,23 @@
|
||||
import { View, Text, Image } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import { getCurrentLocation } from "@/utils/locationUtils";
|
||||
import { View } from "@tarojs/components";
|
||||
import styles from "./index.module.scss";
|
||||
import { useEffect } from "react";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
import { useListState } from "@/store/listStore";
|
||||
|
||||
const ListHeader = () => {
|
||||
const {
|
||||
updateState,
|
||||
location,
|
||||
getLocationText,
|
||||
getLocationLoading,
|
||||
statusNavbarHeightInfo,
|
||||
} = useGlobalState();
|
||||
const { gamesNum } = useListState();
|
||||
console.log("===statusNavbarHeightInfo", statusNavbarHeightInfo);
|
||||
const { statusBarHeight, navbarHeight, totalHeight } = statusNavbarHeightInfo;
|
||||
interface IProps {
|
||||
children: any;
|
||||
}
|
||||
|
||||
// 获取位置信息
|
||||
const getCurrentLocal = () => {
|
||||
updateState({
|
||||
getLocationLoading: true,
|
||||
});
|
||||
getCurrentLocation().then((res) => {
|
||||
updateState({
|
||||
getLocationLoading: false,
|
||||
location: res || {},
|
||||
});
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
// getNavbarHeightInfo();
|
||||
getCurrentLocal();
|
||||
}, []);
|
||||
|
||||
const currentAddress = getLocationLoading
|
||||
? getLocationText
|
||||
: location?.address;
|
||||
const CustomNavbar = (props: IProps) => {
|
||||
const { children } = props;
|
||||
const { statusNavbarHeightInfo } = useGlobalState();
|
||||
const { totalHeight } = statusNavbarHeightInfo;
|
||||
|
||||
return (
|
||||
<View
|
||||
className={styles.customerNavbar}
|
||||
style={{ height: `${totalHeight}px` }}
|
||||
>
|
||||
<View
|
||||
className={styles.container}
|
||||
style={{
|
||||
height: `${navbarHeight}px`,
|
||||
paddingTop: `${statusBarHeight}px`,
|
||||
}}
|
||||
>
|
||||
{/* logo */}
|
||||
<Image src={img.ICON_LOGO} className={styles.logo} />
|
||||
<View className={styles.line} />
|
||||
<View className={styles.content}>
|
||||
<View className={styles.cityWrapper}>
|
||||
{/* 位置 */}
|
||||
<Text className={styles.city}>{currentAddress}</Text>
|
||||
{!getLocationLoading && (
|
||||
<Image src={img.ICON_CHANGE} className={styles.change} />
|
||||
)}
|
||||
</View>
|
||||
<View className={styles.infoWrapper}>
|
||||
<Text className={styles.info}>附近${gamesNum}场球局</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default ListHeader;
|
||||
export default CustomNavbar;
|
||||
|
||||
50
src/components/CustomerNavbarBack/index.module.scss
Normal file
50
src/components/CustomerNavbarBack/index.module.scss
Normal file
@@ -0,0 +1,50 @@
|
||||
.customerNavbarBack {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
background-color: #ffffff;
|
||||
|
||||
.container {
|
||||
padding-left: 17px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 1px;
|
||||
height: 25px;
|
||||
background-color: #0000000F;
|
||||
}
|
||||
|
||||
.back {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.change {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.cityWrapper {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.city {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.infoWrapper {
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
color: #3C3C4399;
|
||||
}
|
||||
}
|
||||
32
src/components/CustomerNavbarBack/index.tsx
Normal file
32
src/components/CustomerNavbarBack/index.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { View, Image } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import styles from "./index.module.scss";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
import Taro from "@tarojs/taro";
|
||||
|
||||
const ListHeader = () => {
|
||||
const { statusNavbarHeightInfo } = useGlobalState();
|
||||
const { statusBarHeight, navbarHeight, totalHeight } = statusNavbarHeightInfo;
|
||||
const handleBack = () => {
|
||||
Taro.navigateBack();
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
className={styles.customerNavbarBack}
|
||||
style={{ height: `${totalHeight}px` }}
|
||||
>
|
||||
<View
|
||||
className={styles.container}
|
||||
style={{
|
||||
height: `${navbarHeight}px`,
|
||||
paddingTop: `${statusBarHeight}px`,
|
||||
}}
|
||||
>
|
||||
{/* back */}
|
||||
<Image src={img.ICON_LIST_SEARCH_BACK} className={styles.back} onClick={handleBack} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
export default ListHeader;
|
||||
@@ -8,10 +8,12 @@ interface IProps {
|
||||
isSelect: boolean;
|
||||
filterCount: number;
|
||||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
onInputClick: () => void;
|
||||
}
|
||||
|
||||
const SearchBarComponent = (props: IProps) => {
|
||||
const { handleFilterIcon, isSelect, filterCount, onChange } = props;
|
||||
const { handleFilterIcon, isSelect, filterCount, onChange, value, onInputClick } = props;
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
onChange && onChange(value);
|
||||
@@ -19,6 +21,7 @@ const SearchBarComponent = (props: IProps) => {
|
||||
return (
|
||||
<>
|
||||
<SearchBar
|
||||
clearable={false}
|
||||
leftIn={
|
||||
<View className={styles.searchBarLeft}>
|
||||
<Image className={styles.searchIcon} src={img.ICON_SEARCH} />
|
||||
@@ -43,6 +46,8 @@ const SearchBarComponent = (props: IProps) => {
|
||||
className={styles.searchBar}
|
||||
placeholder="搜索上海的球局和场地"
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
onInputClick={onInputClick}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -46,4 +46,10 @@ export default {
|
||||
ICON_LIST_PLAYING_GAME: require('@/static/list/icon-paying-game.svg'),
|
||||
ICON_LIST_LOAD_ERROR: require('@/static/list/icon-load-error.svg'),
|
||||
ICON_LIST_RELOAD: require('@/static/list/icon-reload.svg'),
|
||||
ICON_LIST_SEARCH_SEARCH: require('@/static/search/icon-search.svg'),
|
||||
ICON_LIST_SEARCH_BACK: require('@/static/search/icon-back.svg'),
|
||||
ICON_LIST_SEARCH_CLEAR: require('@/static/search/icon-search-clear.svg'),
|
||||
ICON_LIST_SEARCH_CLEAR_HISTORY: require('@/static/search/icon-clear-history.svg'),
|
||||
ICON_LIST_SEARCH_SUGGESTION: require('@/static/search/icon-search-suggestion.svg'),
|
||||
ICON_LIST_INPUT_LOGO: require('@/static/list/icon-input-logo.svg'),
|
||||
}
|
||||
47
src/container/inputCustomerNavbar/index.scss
Normal file
47
src/container/inputCustomerNavbar/index.scss
Normal file
@@ -0,0 +1,47 @@
|
||||
.inputCustomerNavbarContainer {
|
||||
padding-left: 17px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
|
||||
.logo {
|
||||
width: 28px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.icon16 {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.navContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
width: 73%;
|
||||
height: max-content;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.searchContainer {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5.85px;
|
||||
padding: 7.8px;
|
||||
border-radius: 30px;
|
||||
border: 0.488px solid rgba(0, 0, 0, 0.06);
|
||||
box-shadow: 0 3.902px 46.829px 0 rgba(0, 0, 0, 0.08);
|
||||
height: 30px;
|
||||
box-sizing: border-box;
|
||||
font-size: 13.659px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 17.561px;
|
||||
flex: 1;
|
||||
|
||||
.nut-input {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/container/inputCustomerNavbar/index.tsx
Normal file
67
src/container/inputCustomerNavbar/index.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { View, Image } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import { getCurrentLocation } from "@/utils/locationUtils";
|
||||
import "./index.scss";
|
||||
import { useEffect } from "react";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
import { useListState } from "@/store/listStore";
|
||||
import CustomNavbar from "@/components/CustomNavbar";
|
||||
import { Input } from "@nutui/nutui-react-taro";
|
||||
|
||||
interface IProps {
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const ListHeader = (props: IProps) => {
|
||||
const { icon } = props;
|
||||
const { updateState, statusNavbarHeightInfo } = useGlobalState();
|
||||
const { searchValue } = useListState();
|
||||
const { statusBarHeight, navbarHeight } = statusNavbarHeightInfo;
|
||||
|
||||
// 获取位置信息
|
||||
const getCurrentLocal = () => {
|
||||
updateState({
|
||||
getLocationLoading: true,
|
||||
});
|
||||
getCurrentLocation().then((res) => {
|
||||
updateState({
|
||||
getLocationLoading: false,
|
||||
location: res || {},
|
||||
});
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
getCurrentLocal();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CustomNavbar>
|
||||
<View
|
||||
className="inputCustomerNavbarContainer"
|
||||
style={{
|
||||
height: `${navbarHeight}px`,
|
||||
paddingTop: `${statusBarHeight}px`,
|
||||
}}
|
||||
>
|
||||
<View className="navContent">
|
||||
{/* logo */}
|
||||
<Image src={icon} className="logo" />
|
||||
{/* 搜索框 */}
|
||||
<View className="searchContainer">
|
||||
<Image
|
||||
className="searchIcon icon16"
|
||||
src={img.ICON_LIST_SEARCH_SEARCH}
|
||||
/>
|
||||
<Input
|
||||
// className="inputSearch"
|
||||
placeholder="搜索上海的球局和场地"
|
||||
clearable={false}
|
||||
value={searchValue}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</CustomNavbar>
|
||||
);
|
||||
};
|
||||
export default ListHeader;
|
||||
@@ -2,12 +2,9 @@ import { View } from "@tarojs/components";
|
||||
import ListCard from "@/components/ListCard";
|
||||
import ListLoadError from "@/components/ListLoadError";
|
||||
import ListCardSkeleton from "@/components/ListCardSkeleton";
|
||||
// import { useGlobalState } from "@/store/global";
|
||||
|
||||
const ListContainer = (props) => {
|
||||
const { loading, data = [], error, reload } = props;
|
||||
// const { statusNavbarHeightInfo } = useGlobalState() || {};
|
||||
// const { totalHeight } = statusNavbarHeightInfo;
|
||||
|
||||
const renderList = () => {
|
||||
if (loading && data.length > 0) {
|
||||
|
||||
43
src/container/listCustomNavbar/index.module.scss
Normal file
43
src/container/listCustomNavbar/index.module.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
.container {
|
||||
padding-left: 17px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.line {
|
||||
width: 1px;
|
||||
height: 25px;
|
||||
background-color: #0000000F;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.change {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.cityWrapper {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.city {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.infoWrapper {
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-weight: 400;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
color: #3C3C4399;
|
||||
}
|
||||
}
|
||||
69
src/container/listCustomNavbar/index.tsx
Normal file
69
src/container/listCustomNavbar/index.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { View, Text, Image } from "@tarojs/components";
|
||||
import img from "@/config/images";
|
||||
import { getCurrentLocation } from "@/utils/locationUtils";
|
||||
import styles from "./index.module.scss";
|
||||
import { useEffect } from "react";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
import { useListState } from "@/store/listStore";
|
||||
import CustomNavbar from '@/components/CustomNavbar'
|
||||
|
||||
const ListHeader = () => {
|
||||
const {
|
||||
updateState,
|
||||
location,
|
||||
getLocationText,
|
||||
getLocationLoading,
|
||||
statusNavbarHeightInfo,
|
||||
} = useGlobalState();
|
||||
const { gamesNum } = useListState();
|
||||
const { statusBarHeight, navbarHeight } = statusNavbarHeightInfo;
|
||||
|
||||
// 获取位置信息
|
||||
const getCurrentLocal = () => {
|
||||
updateState({
|
||||
getLocationLoading: true,
|
||||
});
|
||||
getCurrentLocation().then((res) => {
|
||||
updateState({
|
||||
getLocationLoading: false,
|
||||
location: res || {},
|
||||
});
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
getCurrentLocal();
|
||||
}, []);
|
||||
|
||||
const currentAddress = getLocationLoading
|
||||
? getLocationText
|
||||
: location?.address;
|
||||
|
||||
return (
|
||||
<CustomNavbar>
|
||||
<View
|
||||
className={styles.container}
|
||||
style={{
|
||||
height: `${navbarHeight}px`,
|
||||
paddingTop: `${statusBarHeight}px`,
|
||||
}}
|
||||
>
|
||||
{/* logo */}
|
||||
<Image src={img.ICON_LOGO} className={styles.logo} />
|
||||
<View className={styles.line} />
|
||||
<View className={styles.content}>
|
||||
<View className={styles.cityWrapper}>
|
||||
{/* 位置 */}
|
||||
<Text className={styles.city}>{currentAddress}</Text>
|
||||
{!getLocationLoading && (
|
||||
<Image src={img.ICON_CHANGE} className={styles.change} />
|
||||
)}
|
||||
</View>
|
||||
<View className={styles.infoWrapper}>
|
||||
<Text className={styles.info}>附近${gamesNum}场球局</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</CustomNavbar>
|
||||
);
|
||||
};
|
||||
export default ListHeader;
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
.listTopSearchWrapper {
|
||||
padding: 0 15px;
|
||||
position: sticky;
|
||||
// position: sticky;
|
||||
background: #fefefe;
|
||||
z-index: 999;
|
||||
// z-index: 999;
|
||||
}
|
||||
|
||||
.isScroll {
|
||||
border-bottom: 0.5px solid #0000000F;
|
||||
}
|
||||
// .isScroll {
|
||||
// border-bottom: 0.5px solid #0000000F;
|
||||
// }
|
||||
|
||||
.listTopFilterWrapper {
|
||||
display: flex;
|
||||
|
||||
@@ -8,15 +8,18 @@ import Taro, { usePageScroll, useReachBottom } from "@tarojs/taro";
|
||||
import { useListStore } from "@/store/listStore";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
import { View } from "@tarojs/components";
|
||||
import CustomerNavBar from "@/components/CustomNavbar";
|
||||
import CustomerNavBar from "@/container/listCustomNavbar";
|
||||
import InputCustomerBar from "@/container/inputCustomerNavbar";
|
||||
import GuideBar from "@/components/GuideBar";
|
||||
import ListContainer from "@/container/listContainer";
|
||||
import img from "@/config/images";
|
||||
|
||||
const ListPage = () => {
|
||||
// 从 store 获取数据和方法
|
||||
const store = useListStore() || {};
|
||||
|
||||
const { statusNavbarHeightInfo } = useGlobalState() || {};
|
||||
const { totalHeight } = statusNavbarHeightInfo || {};
|
||||
const {
|
||||
isShowFilterPopup,
|
||||
error,
|
||||
@@ -33,11 +36,18 @@ const ListPage = () => {
|
||||
quickFilterData,
|
||||
distanceQuickFilter,
|
||||
isScrollTop,
|
||||
searchValue,
|
||||
isShowInputCustomerNavBar,
|
||||
} = store;
|
||||
|
||||
usePageScroll((res) => {
|
||||
if (res?.scrollTop > 0 && !isScrollTop) {
|
||||
updateState({ isScrollTop: true });
|
||||
// if (res?.scrollTop > 0 && !isScrollTop) {
|
||||
// updateState({ isScrollTop: true });
|
||||
// }
|
||||
if (res?.scrollTop >= totalHeight && !isScrollTop) {
|
||||
updateState({ isShowInputCustomerNavBar: true });
|
||||
} else {
|
||||
updateState({ isShowInputCustomerNavBar: false });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -105,28 +115,40 @@ const ListPage = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleSearchClick = () => {
|
||||
Taro.navigateTo({
|
||||
url: "/pages/search/index",
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomerNavBar />
|
||||
{!isShowInputCustomerNavBar ? (
|
||||
<CustomerNavBar />
|
||||
) : (
|
||||
<InputCustomerBar icon={img.ICON_LIST_INPUT_LOGO} />
|
||||
)}
|
||||
|
||||
<View className={styles.listPage}>
|
||||
<View
|
||||
className={`${styles.listTopSearchWrapper} ${
|
||||
isScrollTop ? styles.isScroll : ""
|
||||
}`}
|
||||
style={{
|
||||
top: statusNavbarHeightInfo?.totalHeight,
|
||||
}}
|
||||
// style={{
|
||||
// top: statusNavbarHeightInfo?.totalHeight,
|
||||
// }}
|
||||
>
|
||||
<SearchBar
|
||||
handleFilterIcon={toggleShowPopup}
|
||||
isSelect={filterCount > 0}
|
||||
filterCount={filterCount}
|
||||
onChange={handleSearchChange}
|
||||
value={searchValue}
|
||||
onInputClick={handleSearchClick}
|
||||
/>
|
||||
{/* 综合筛选 */}
|
||||
{isShowFilterPopup && (
|
||||
<div>
|
||||
<View>
|
||||
<FilterPopup
|
||||
loading={loading}
|
||||
onCancel={toggleShowPopup}
|
||||
@@ -138,7 +160,7 @@ const ListPage = () => {
|
||||
onClose={toggleShowPopup}
|
||||
statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight}
|
||||
/>
|
||||
</div>
|
||||
</View>
|
||||
)}
|
||||
{/* 筛选 */}
|
||||
<div className={styles.listTopFilterWrapper}>
|
||||
@@ -161,13 +183,13 @@ const ListPage = () => {
|
||||
</div>
|
||||
</View>
|
||||
|
||||
{/* 列表内容 */}
|
||||
<ListContainer
|
||||
data={matches}
|
||||
loading={loading}
|
||||
error={error}
|
||||
reload={refreshMatches}
|
||||
/>
|
||||
{/* 列表内容 */}
|
||||
<ListContainer
|
||||
data={matches}
|
||||
loading={loading}
|
||||
error={error}
|
||||
reload={refreshMatches}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<GuideBar currentPage="list" />
|
||||
|
||||
4
src/pages/search/index.config.ts
Normal file
4
src/pages/search/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '',
|
||||
navigationStyle: 'custom',
|
||||
})
|
||||
120
src/pages/search/index.scss
Normal file
120
src/pages/search/index.scss
Normal file
@@ -0,0 +1,120 @@
|
||||
.listSearchContainer {
|
||||
padding: 0 15px;
|
||||
|
||||
.icon16 {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.topSearch {
|
||||
padding: 10px 16px 5px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
box-sizing: border-box;
|
||||
gap: 10px;
|
||||
border-radius: 44px;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||
background: #FFF;
|
||||
box-shadow: 0 4px 48px 0 rgba(0, 0, 0, 0.08);
|
||||
|
||||
.nut-input {
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.searchRight {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.searchLine {
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
border-radius: 20px;
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.searchText {
|
||||
color: #000000;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 20px
|
||||
}
|
||||
}
|
||||
|
||||
.searchIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.historySearchTitleWrapper {
|
||||
display: flex;
|
||||
padding: 12px 15px;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
align-self: stretch;
|
||||
|
||||
.historySearchTitle,
|
||||
.historySearchClear {
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.historySearchClear {
|
||||
color: #9a9a9a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.historySearchList {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.historySearchItem {
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
display: flex;
|
||||
height: 28px;
|
||||
padding: 4px 12px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
border-radius: 999px;
|
||||
border: 0.5px solid rgba(0, 0, 0, 0.06);
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
}
|
||||
|
||||
.searchSuggestion {
|
||||
padding: 6px 0;
|
||||
|
||||
.searchSuggestionItem {
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.searchSuggestionItemLeft {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: rgba(60, 60, 67, 0.60);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
201
src/pages/search/index.tsx
Normal file
201
src/pages/search/index.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import InputCustomerBar from "@/container/inputCustomerNavbar";
|
||||
import CustomerNavbarBack from "@/components/CustomerNavbarBack";
|
||||
import { View, Image, Text } from "@tarojs/components";
|
||||
import { Input } from "@nutui/nutui-react-taro";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { useListState } from "@/store/listStore";
|
||||
import img from "@/config/images";
|
||||
import "./index.scss";
|
||||
import Taro from "@tarojs/taro";
|
||||
|
||||
const ListSearch = () => {
|
||||
const {
|
||||
searchValue,
|
||||
updateState,
|
||||
getSearchHistory,
|
||||
searchHistory = [],
|
||||
clearHistory,
|
||||
searchSuggestion,
|
||||
suggestionList,
|
||||
isShowSuggestion,
|
||||
isShowInputCustomerNavBar,
|
||||
} = useListState() || {};
|
||||
|
||||
const ref = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getSearchHistory();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref?.current) {
|
||||
ref.current.focus();
|
||||
}
|
||||
}, [ref.current]);
|
||||
|
||||
const regex = useMemo(() => {
|
||||
return new RegExp(searchValue, "gi");
|
||||
}, [searchValue]);
|
||||
|
||||
/**
|
||||
* @description 输入
|
||||
* @param value
|
||||
*/
|
||||
const handleChange = (value: string) => {
|
||||
updateState({ searchValue: value });
|
||||
if (value) {
|
||||
searchSuggestion(value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 点击清空输入内容
|
||||
*/
|
||||
const handleClear = () => {
|
||||
updateState({ searchValue: "" });
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 点击历史搜索
|
||||
* @param value
|
||||
*/
|
||||
const handleHistoryClick = (value: string) => {
|
||||
updateState({ searchValue: value });
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 清空历史搜索
|
||||
*/
|
||||
const handleClearHistory = () => {
|
||||
clearHistory();
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 点击联想词
|
||||
*/
|
||||
const handleSuggestionSearch = (val: string) => {
|
||||
updateState({
|
||||
searchValue: val,
|
||||
isShowSuggestion: false,
|
||||
});
|
||||
handleSearch();
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 点击搜索
|
||||
*/
|
||||
const handleSearch = () => {
|
||||
Taro.navigateTo({
|
||||
url: `/pages/searchResult/index`,
|
||||
});
|
||||
}
|
||||
|
||||
// 是否显示清空图标
|
||||
const isShowClearIcon = searchValue && searchValue?.length > 0;
|
||||
|
||||
// 是否显示搜索历史
|
||||
const isShowHistory =
|
||||
!isShowClearIcon && searchHistory && searchHistory?.length > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isShowInputCustomerNavBar ? (
|
||||
<CustomerNavbarBack />
|
||||
) : (
|
||||
<InputCustomerBar icon={img.ICON_LIST_INPUT_LOGO} />
|
||||
)}
|
||||
<View className="listSearchContainer">
|
||||
{/* 搜索 */}
|
||||
<View className="topSearch">
|
||||
<Image className="searchIcon" src={img.ICON_LIST_SEARCH_SEARCH} />
|
||||
<Input
|
||||
placeholder="搜索上海的球局和场地"
|
||||
value={searchValue}
|
||||
defaultValue={searchValue}
|
||||
onChange={handleChange}
|
||||
onClear={handleClear}
|
||||
autoFocus
|
||||
clearable={false}
|
||||
ref={ref}
|
||||
/>
|
||||
<View className="searchRight">
|
||||
{isShowClearIcon && (
|
||||
<Image
|
||||
className="clearIcon icon16"
|
||||
src={img.ICON_LIST_SEARCH_CLEAR}
|
||||
onClick={handleClear}
|
||||
/>
|
||||
)}
|
||||
<View className="searchLine" />
|
||||
<Text className="searchText" onClick={handleSearch}>搜索</Text>
|
||||
</View>
|
||||
</View>
|
||||
{/* 联想词 */}
|
||||
{isShowSuggestion && (
|
||||
<View className="searchSuggestion">
|
||||
{(suggestionList || [])?.map((item) => {
|
||||
// 替换匹配文本为高亮版本
|
||||
const highlightedText = item.replace(regex, (match) => {
|
||||
// 如果匹配不到,则返回原文本
|
||||
if (!match) return match;
|
||||
return `<Text class="highlight">${match}</Text>`;
|
||||
});
|
||||
return (
|
||||
<View
|
||||
className="searchSuggestionItem"
|
||||
onClick={() => handleSuggestionSearch(item)}
|
||||
>
|
||||
<View className="searchSuggestionItemLeft">
|
||||
<Image
|
||||
className="icon16"
|
||||
src={img.ICON_LIST_SEARCH_SEARCH}
|
||||
/>
|
||||
<Text
|
||||
dangerouslySetInnerHTML={{ __html: highlightedText }}
|
||||
></Text>
|
||||
</View>
|
||||
<Image
|
||||
className="icon16"
|
||||
src={img.ICON_LIST_SEARCH_SUGGESTION}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
{/* 历史搜索 */}
|
||||
{!isShowClearIcon && (
|
||||
<View className="historySearch">
|
||||
<View className="historySearchTitleWrapper">
|
||||
<View className="historySearchTitle">历史搜索</View>
|
||||
<View className="historySearchClear" onClick={handleClearHistory}>
|
||||
<Text>清空</Text>
|
||||
<Image
|
||||
className="clearIcon icon16"
|
||||
src={img.ICON_LIST_SEARCH_CLEAR_HISTORY}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{isShowHistory && (
|
||||
<View className="historySearchList">
|
||||
{(searchHistory || [])?.map((item) => {
|
||||
return (
|
||||
<Text
|
||||
className="historySearchItem"
|
||||
onClick={() => handleHistoryClick(item)}
|
||||
>
|
||||
{item}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default ListSearch;
|
||||
3
src/pages/searchResult/index.scss
Normal file
3
src/pages/searchResult/index.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.menuFilter {
|
||||
color: red;
|
||||
}
|
||||
65
src/pages/searchResult/index.tsx
Normal file
65
src/pages/searchResult/index.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
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 ListContainer from "@/container/listContainer";
|
||||
|
||||
import "./index.scss";
|
||||
|
||||
const SearchResult = () => {
|
||||
const {
|
||||
distanceData,
|
||||
quickFilterData,
|
||||
distanceQuickFilter,
|
||||
updateState,
|
||||
matches,
|
||||
loading,
|
||||
error,
|
||||
refreshMatches,
|
||||
} = useListState() || {};
|
||||
|
||||
// 距离筛选
|
||||
const handleDistanceOrQuickChange = (name, value) => {
|
||||
updateState({
|
||||
distanceQuickFilter: {
|
||||
...distanceQuickFilter,
|
||||
[name]: value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
{/* 筛选 */}
|
||||
<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>
|
||||
|
||||
{/* 列表内容 */}
|
||||
<ListContainer
|
||||
data={matches}
|
||||
loading={loading}
|
||||
error={error}
|
||||
reload={refreshMatches}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchResult;
|
||||
@@ -1,4 +1,3 @@
|
||||
import { TennisMatch } from "../store/listStore";
|
||||
import httpService from "./httpService";
|
||||
|
||||
// 模拟网络延迟
|
||||
@@ -12,57 +11,6 @@ interface ApiResponse<T> {
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
// 模拟网球比赛数据
|
||||
const mockTennisMatches: TennisMatch[] = [
|
||||
{
|
||||
id: "1",
|
||||
title: "周一晚场浦东新区单打约球",
|
||||
dateTime: "明天(周五)下午5点 2小时",
|
||||
location: "仁恒河滨花园网球场",
|
||||
distance: "3.5km",
|
||||
shinei: "室内",
|
||||
registeredCount: 3,
|
||||
maxCount: 4,
|
||||
skillLevel: "2.0 至 2.5",
|
||||
matchType: "双打",
|
||||
images: [
|
||||
"https://images.unsplash.com/photo-1554068865-24cecd4e34b8?w=200&h=200&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=200&h=200&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1551698618-1dfe5d97d256?w=200&h=200&fit=crop&crop=center",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "浦东新区单打约球",
|
||||
dateTime: "明天(周五)下午5点 2小时",
|
||||
location: "仁恒河滨花园网球场",
|
||||
distance: "3.5km",
|
||||
shinei: "室外",
|
||||
registeredCount: 2,
|
||||
maxCount: 4,
|
||||
skillLevel: "2.0 至 2.5",
|
||||
matchType: "双打",
|
||||
images: [
|
||||
"https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=200&h=200&fit=crop&crop=center",
|
||||
"https://images.unsplash.com/photo-1551698618-1dfe5d97d256?w=200&h=200&fit=crop&crop=center",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
title: "黄浦区双打约球",
|
||||
dateTime: "7月20日(周日)下午6点 2小时",
|
||||
location: "仁恒河滨花园网球场",
|
||||
distance: "3.5km",
|
||||
registeredCount: 3,
|
||||
maxCount: 4,
|
||||
skillLevel: "2.0 至 2.5",
|
||||
matchType: "双打",
|
||||
images: [
|
||||
"https://images.unsplash.com/photo-1554068865-24cecd4e34b8?w=200&h=200&fit=crop&crop=center",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 获取网球比赛列表
|
||||
* @param params 查询参数
|
||||
@@ -86,10 +34,10 @@ export const getTennisMatches = async (params?: {
|
||||
* 刷新网球比赛数据
|
||||
* @returns Promise<TennisMatch[]>
|
||||
*/
|
||||
export const refreshTennisMatches = async (): Promise<TennisMatch[]> => {
|
||||
export const refreshTennisMatches = async (params) => {
|
||||
try {
|
||||
// 生成新的动态数据
|
||||
const matches = generateDynamicData();
|
||||
const matches = generateDynamicData(params);
|
||||
return matches;
|
||||
} catch (error) {
|
||||
console.error("API刷新失败:", error);
|
||||
@@ -97,3 +45,52 @@ export const refreshTennisMatches = async (): Promise<TennisMatch[]> => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取搜索历史记录的异步函数
|
||||
* @param {Object} params - 查询参数对象
|
||||
* @returns {Promise} - 返回一个Promise对象,包含获取到的搜索历史数据
|
||||
*/
|
||||
export const getSearchHistory = async (params) => {
|
||||
try {
|
||||
// 调用HTTP服务获取搜索历史记录
|
||||
return httpService.get('/games/search_history', params)
|
||||
} catch (error) {
|
||||
// 捕获并打印错误信息
|
||||
console.error("历史记录获取失败:", error);
|
||||
// 抛出错误以便上层处理
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 清除搜索历史
|
||||
* @returns
|
||||
*/
|
||||
export const clearHistory = async () => {
|
||||
try {
|
||||
// 调用HTTP服务清除搜索历史记录
|
||||
return httpService.post('/games/clear_history')
|
||||
} catch (error) {
|
||||
// 捕获并打印错误信息
|
||||
console.error("清除历史记录失败:", error);
|
||||
// 抛出错误以便上层处理
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取联想
|
||||
* @param params 查询参数
|
||||
* @returns
|
||||
*/
|
||||
export const searchSuggestion = async (params) => {
|
||||
try {
|
||||
// 调用HTTP服务获取搜索建议
|
||||
return httpService.get('/games/search_suggestion', params)
|
||||
} catch (error) {
|
||||
// 捕获并打印错误信息
|
||||
console.error("搜索建议获取失败:", error);
|
||||
// 抛出错误以便上层处理
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
3
src/static/list/icon-input-logo.svg
Normal file
3
src/static/list/icon-input-logo.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="27" height="13" viewBox="0 0 27 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.894158 7.15855V5.83157C0.894158 2.78355 2.80822 0.829274 5.87233 0.829274C7.08671 0.829274 8.21263 1.15096 9.03295 1.67371C9.83717 2.17233 10.3519 2.85593 10.3519 3.55561C10.3519 4.2955 9.82913 4.8102 9.0812 4.8102C8.75951 4.8102 8.49411 4.71369 8.26089 4.55285C7.60946 4.11857 7.09476 3.48323 6.12164 3.48323C4.74641 3.48323 4.07086 4.31962 4.07086 6.00046V7.15051C4.07086 8.80721 4.74641 9.68382 6.04122 9.68382C7.00629 9.68382 7.65772 9.12086 7.65772 8.30859V8.08341H7.16714C6.37095 8.08341 5.93667 7.69738 5.93667 6.9977C5.93667 6.29802 6.36291 5.93612 7.16714 5.93612H8.91231C10.1187 5.93612 10.6414 6.44278 10.6414 7.59283V8.02711C10.6414 10.5765 8.77559 12.2413 5.9045 12.2413C2.75192 12.2413 0.894158 10.3352 0.894158 7.15855ZM11.9251 7.15051V5.96025C11.9251 2.75138 13.7668 0.829274 16.9596 0.829274C20.1523 0.829274 21.986 2.75942 21.986 5.96025V7.15051C21.986 10.3352 20.1523 12.2413 16.9596 12.2413C13.7668 12.2413 11.9251 10.3272 11.9251 7.15051ZM15.1018 5.93612V7.14246C15.1018 8.75896 15.7854 9.69187 16.9596 9.69187C18.1337 9.69187 18.8093 8.75896 18.8093 7.14246V5.93612C18.8093 4.31962 18.1337 3.37868 16.9596 3.37868C15.7854 3.37868 15.1018 4.31962 15.1018 5.93612ZM23.8165 7.39982L23.5833 2.24471C23.5431 1.35202 24.2267 0.620174 25.1998 0.620174C26.1729 0.620174 26.8726 1.35202 26.8244 2.24471L26.567 7.39982C26.5188 8.17992 25.9317 8.69462 25.1918 8.69462C24.4358 8.69462 23.8487 8.14775 23.8165 7.39982ZM23.5994 10.7454C23.5994 9.8688 24.3232 9.32996 25.2079 9.32996C26.0845 9.32996 26.8002 9.8688 26.8002 10.7454C26.8002 11.6301 26.0845 12.1689 25.2079 12.1689C24.3232 12.1689 23.5994 11.6301 23.5994 10.7454Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
3
src/static/search/icon-back.svg
Normal file
3
src/static/search/icon-back.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
|
||||
<path d="M20.6667 24L12.6667 16L20.6667 8" stroke="black" stroke-width="2.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 234 B |
8
src/static/search/icon-clear-history.svg
Normal file
8
src/static/search/icon-clear-history.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66669 1.97144H9.33335V4.63812H14.3334V7.30478H1.66669V4.63812H6.66669V1.97144Z" stroke="#9A9A9A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.66669 13.3333H13.3334V7.33325H2.66669V13.3333Z" stroke="#9A9A9A" stroke-width="1.33333" stroke-linejoin="round"/>
|
||||
<path d="M5.33331 13.2992V11.3047" stroke="#9A9A9A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M8 13.2993V11.2993" stroke="#9A9A9A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.6667 13.2992V11.3047" stroke="#9A9A9A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 13.3333H12" stroke="#9A9A9A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 929 B |
14
src/static/search/icon-search-clear.svg
Normal file
14
src/static/search/icon-search-clear.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_3385_5398)">
|
||||
<path d="M7.99998 14.6666C11.6819 14.6666 14.6666 11.6818 14.6666 7.99992C14.6666 4.31802 11.6819 1.33325 7.99998 1.33325C4.31808 1.33325 1.33331 4.31802 1.33331 7.99992C1.33331 11.6818 4.31808 14.6666 7.99998 14.6666Z" fill="#A0A0A0" stroke="#A0A0A0" stroke-width="1.33333" stroke-linejoin="round"/>
|
||||
<path d="M9.88555 6.1145L6.11432 9.88574L9.88555 6.1145Z" fill="white"/>
|
||||
<path d="M9.88555 6.1145L6.11432 9.88574" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.11444 6.1145L9.88567 9.88574L6.11444 6.1145Z" fill="white"/>
|
||||
<path d="M6.11444 6.1145L9.88567 9.88574" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3385_5398">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 954 B |
4
src/static/search/icon-search-suggestion.svg
Normal file
4
src/static/search/icon-search-suggestion.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.33337 3.66675H12.3334V9.66675" stroke="#9A9A9A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.84802 12.152L12.3333 3.66675" stroke="#9A9A9A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 366 B |
4
src/static/search/icon-search.svg
Normal file
4
src/static/search/icon-search.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M8.75002 15.8334C12.662 15.8334 15.8334 12.6621 15.8334 8.75008C15.8334 4.83808 12.662 1.66675 8.75002 1.66675C4.83802 1.66675 1.66669 4.83808 1.66669 8.75008C1.66669 12.6621 4.83802 15.8334 8.75002 15.8334Z" stroke="black" stroke-width="1.66667" stroke-linejoin="round"/>
|
||||
<path d="M13.8423 13.8423L17.3779 17.3778" stroke="black" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 518 B |
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand'
|
||||
import { getTennisMatches } from '../services/listApi'
|
||||
import { getTennisMatches, getSearchHistory, clearHistory, searchSuggestion } from '../services/listApi'
|
||||
import { ListActions, IFilterOptions, ListState } from '../../types/list/types'
|
||||
|
||||
// 完整的 Store 类型
|
||||
@@ -21,7 +21,8 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
matches: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
lastRefreshTime: null,
|
||||
// 搜索的value
|
||||
searchValue: '',
|
||||
// 是否展示综合筛选弹窗
|
||||
isShowFilterPopup: false,
|
||||
// 综合筛选项
|
||||
@@ -77,6 +78,19 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
gamesNum: 124,
|
||||
// 页面滚动距离顶部距离 是否大于0
|
||||
isScrollTop: false,
|
||||
// 搜索历史数据
|
||||
searchHistory: ['上海', '黄浦', '上海', '静安', '徐汇', '黄浦', '普陀', '黄浦', '长宁', '黄浦'],
|
||||
// 搜索历史数据默认 Top 15
|
||||
searchHistoryParams: {
|
||||
page: 1,
|
||||
pageSize: 15,
|
||||
},
|
||||
// 联想词
|
||||
suggestionList: [],
|
||||
// 是否显示联想词
|
||||
isShowSuggestion: false,
|
||||
// 是否显示搜索框自定义导航
|
||||
isShowInputCustomerNavBar: false,
|
||||
|
||||
// 获取比赛数据
|
||||
fetchMatches: async (params) => {
|
||||
@@ -135,12 +149,51 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
set({
|
||||
matches: rows,
|
||||
loading: false,
|
||||
lastRefreshTime: new Date().toISOString()
|
||||
})
|
||||
} catch (error) {
|
||||
}
|
||||
},
|
||||
|
||||
// 获取历史搜索数据
|
||||
getSearchHistory: async () => {
|
||||
try {
|
||||
const params = get()?.searchHistoryParams || {}
|
||||
const resData = await getSearchHistory(params) || {};
|
||||
console.log('===resData', resData)
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
// 清空历史记录
|
||||
clearHistory: async () => {
|
||||
try {
|
||||
const resData = await clearHistory() || {};
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
set({
|
||||
searchHistory: [],
|
||||
})
|
||||
},
|
||||
|
||||
// 获取联想
|
||||
searchSuggestion: async (val: string) => {
|
||||
try {
|
||||
const resData = await searchSuggestion({ val }) || {};
|
||||
console.log('===获取联想', resData)
|
||||
// set({
|
||||
// suggestionList: ['上海球局', '黄浦球局', '上海球局', '静安球局', '徐汇球局', '黄浦球局', '普陀球局', '黄浦球局', '长宁球局', '黄浦球局'],
|
||||
// isShowSuggestion: true,
|
||||
// })
|
||||
} catch (error) {
|
||||
set({
|
||||
suggestionList: ['上海球局', '黄浦球局', '上海球局', '静安球局', '徐汇球局', '黄浦球局', '普陀球局', '黄浦球局', '长宁球局', '黄浦球局'],
|
||||
isShowSuggestion: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 清除错误信息
|
||||
clearError: () => {
|
||||
set({ error: null })
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface ListState {
|
||||
matches: TennisMatch[]
|
||||
loading: boolean
|
||||
error: string | null
|
||||
lastRefreshTime: string | null
|
||||
searchValue: string
|
||||
isShowFilterPopup: boolean
|
||||
filterOptions: IFilterOptions
|
||||
filterCount: number
|
||||
@@ -40,24 +40,11 @@ export interface ListState {
|
||||
gamePlayOptions: BubbleOption[]
|
||||
gamesNum: number
|
||||
isScrollTop: boolean
|
||||
}
|
||||
|
||||
export interface ListState {
|
||||
matches: TennisMatch[]
|
||||
loading: boolean
|
||||
error: string | null
|
||||
lastRefreshTime: string | null
|
||||
isShowFilterPopup: boolean
|
||||
filterOptions: IFilterOptions
|
||||
filterCount: number
|
||||
distance: string | number
|
||||
quickFilter: string | number
|
||||
distanceData: any[]
|
||||
quickFilterData: any[]
|
||||
distanceQuickFilter: {
|
||||
distance: string
|
||||
quick: string
|
||||
}
|
||||
searchHistoryParams: Record<string, any>
|
||||
searchHistory: string[]
|
||||
suggestionList: string[]
|
||||
isShowSuggestion: boolean
|
||||
isShowInputCustomerNavBar: boolean
|
||||
}
|
||||
|
||||
export interface ListActions {
|
||||
@@ -72,6 +59,9 @@ export interface ListActions {
|
||||
updateState: (payload: Record<string, any>) => void
|
||||
updateFilterOptions: (payload: Record<string, any>) => void
|
||||
clearFilterOptions: () => void
|
||||
getSearchHistory: () => Promise<void>
|
||||
clearHistory: () => void
|
||||
searchSuggestion: (val: string) => Promise<void>
|
||||
}
|
||||
|
||||
// 快捷筛选
|
||||
|
||||
Reference in New Issue
Block a user