diff --git a/src/app.config.ts b/src/app.config.ts index a6b01d2..ae26c3e 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -1,8 +1,9 @@ export default defineAppConfig({ pages: [ - 'pages/list/index', 'pages/publishBall/index', - + 'pages/list/index', // 列表页 + 'pages/search/index', // 搜索页 + 'pages/searchResult/index', // 搜索结果页面 // 'pages/userInfo/myself/index', 'pages/login/index/index', 'pages/login/verification/index', diff --git a/src/components/CustomNavbar/index.module.scss b/src/components/CustomNavbar/index.module.scss index 9c921b2..b15b2d5 100644 --- a/src/components/CustomNavbar/index.module.scss +++ b/src/components/CustomNavbar/index.module.scss @@ -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; - } } \ No newline at end of file diff --git a/src/components/CustomNavbar/index.tsx b/src/components/CustomNavbar/index.tsx index 45f96fb..5ad3864 100644 --- a/src/components/CustomNavbar/index.tsx +++ b/src/components/CustomNavbar/index.tsx @@ -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 ( - - {/* logo */} - - - - - {/* 位置 */} - {currentAddress} - {!getLocationLoading && ( - - )} - - - 附近${gamesNum}场球局 - - - + {children} ); }; -export default ListHeader; +export default CustomNavbar; diff --git a/src/components/CustomerNavbarBack/index.module.scss b/src/components/CustomerNavbarBack/index.module.scss new file mode 100644 index 0000000..bc09a72 --- /dev/null +++ b/src/components/CustomerNavbarBack/index.module.scss @@ -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; + } +} \ No newline at end of file diff --git a/src/components/CustomerNavbarBack/index.tsx b/src/components/CustomerNavbarBack/index.tsx new file mode 100644 index 0000000..338ad51 --- /dev/null +++ b/src/components/CustomerNavbarBack/index.tsx @@ -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 ( + + + {/* back */} + + + + ); +}; +export default ListHeader; diff --git a/src/components/SearchBar/index.tsx b/src/components/SearchBar/index.tsx index 8181030..f0aa118 100644 --- a/src/components/SearchBar/index.tsx +++ b/src/components/SearchBar/index.tsx @@ -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 ( <> @@ -43,6 +46,8 @@ const SearchBarComponent = (props: IProps) => { className={styles.searchBar} placeholder="搜索上海的球局和场地" onChange={handleChange} + value={value} + onInputClick={onInputClick} /> ); diff --git a/src/config/images.js b/src/config/images.js index ea42fd9..8009e5f 100644 --- a/src/config/images.js +++ b/src/config/images.js @@ -50,4 +50,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'), } \ No newline at end of file diff --git a/src/container/inputCustomerNavbar/index.scss b/src/container/inputCustomerNavbar/index.scss new file mode 100644 index 0000000..a92d9cd --- /dev/null +++ b/src/container/inputCustomerNavbar/index.scss @@ -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; + } + } + } \ No newline at end of file diff --git a/src/container/inputCustomerNavbar/index.tsx b/src/container/inputCustomerNavbar/index.tsx new file mode 100644 index 0000000..d922d2b --- /dev/null +++ b/src/container/inputCustomerNavbar/index.tsx @@ -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 ( + + + + {/* logo */} + + {/* 搜索框 */} + + + + + + + + ); +}; +export default ListHeader; diff --git a/src/container/listContainer/index.tsx b/src/container/listContainer/index.tsx index 5407c87..380b27d 100644 --- a/src/container/listContainer/index.tsx +++ b/src/container/listContainer/index.tsx @@ -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) { diff --git a/src/container/listCustomNavbar/index.module.scss b/src/container/listCustomNavbar/index.module.scss new file mode 100644 index 0000000..b31514b --- /dev/null +++ b/src/container/listCustomNavbar/index.module.scss @@ -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; + } + } \ No newline at end of file diff --git a/src/container/listCustomNavbar/index.tsx b/src/container/listCustomNavbar/index.tsx new file mode 100644 index 0000000..b9484eb --- /dev/null +++ b/src/container/listCustomNavbar/index.tsx @@ -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 ( + + + {/* logo */} + + + + + {/* 位置 */} + {currentAddress} + {!getLocationLoading && ( + + )} + + + 附近${gamesNum}场球局 + + + + + ); +}; +export default ListHeader; diff --git a/src/pages/list/index.module.scss b/src/pages/list/index.module.scss index 7a5f4ac..fb530ec 100644 --- a/src/pages/list/index.module.scss +++ b/src/pages/list/index.module.scss @@ -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; diff --git a/src/pages/list/index.tsx b/src/pages/list/index.tsx index 8e7edb6..779c376 100644 --- a/src/pages/list/index.tsx +++ b/src/pages/list/index.tsx @@ -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 ( <> - + {!isShowInputCustomerNavBar ? ( + + ) : ( + + )} 0} filterCount={filterCount} onChange={handleSearchChange} + value={searchValue} + onInputClick={handleSearchClick} /> {/* 综合筛选 */} {isShowFilterPopup && ( -
+ { onClose={toggleShowPopup} statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight} /> -
+
)} {/* 筛选 */}
@@ -161,13 +183,13 @@ const ListPage = () => {
- {/* 列表内容 */} - + {/* 列表内容 */} +
diff --git a/src/pages/search/index.config.ts b/src/pages/search/index.config.ts new file mode 100644 index 0000000..176e97c --- /dev/null +++ b/src/pages/search/index.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '', + navigationStyle: 'custom', +}) diff --git a/src/pages/search/index.scss b/src/pages/search/index.scss new file mode 100644 index 0000000..5dc03cb --- /dev/null +++ b/src/pages/search/index.scss @@ -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; + } + } + } +} \ No newline at end of file diff --git a/src/pages/search/index.tsx b/src/pages/search/index.tsx new file mode 100644 index 0000000..b84662b --- /dev/null +++ b/src/pages/search/index.tsx @@ -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(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 ? ( + + ) : ( + + )} + + {/* 搜索 */} + + + + + {isShowClearIcon && ( + + )} + + 搜索 + + + {/* 联想词 */} + {isShowSuggestion && ( + + {(suggestionList || [])?.map((item) => { + // 替换匹配文本为高亮版本 + const highlightedText = item.replace(regex, (match) => { + // 如果匹配不到,则返回原文本 + if (!match) return match; + return `${match}`; + }); + return ( + handleSuggestionSearch(item)} + > + + + + + + + ); + })} + + )} + {/* 历史搜索 */} + {!isShowClearIcon && ( + + + 历史搜索 + + 清空 + + + + + {isShowHistory && ( + + {(searchHistory || [])?.map((item) => { + return ( + handleHistoryClick(item)} + > + {item} + + ); + })} + + )} + + )} + + + ); +}; +export default ListSearch; diff --git a/src/pages/searchResult/index.scss b/src/pages/searchResult/index.scss new file mode 100644 index 0000000..7702675 --- /dev/null +++ b/src/pages/searchResult/index.scss @@ -0,0 +1,3 @@ +.menuFilter { + color: red; +} \ No newline at end of file diff --git a/src/pages/searchResult/index.tsx b/src/pages/searchResult/index.tsx new file mode 100644 index 0000000..6dd74eb --- /dev/null +++ b/src/pages/searchResult/index.tsx @@ -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 ( + + {/* 筛选 */} + + {/* 全城筛选 */} + + {/* 智能排序 */} + + + + {/* 列表内容 */} + + + ); +}; + +export default SearchResult; \ No newline at end of file diff --git a/src/services/listApi.ts b/src/services/listApi.ts index de2be2e..414efbb 100644 --- a/src/services/listApi.ts +++ b/src/services/listApi.ts @@ -1,4 +1,3 @@ -import { TennisMatch } from "../store/listStore"; import httpService from "./httpService"; // 模拟网络延迟 @@ -12,57 +11,6 @@ interface ApiResponse { 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 */ -export const refreshTennisMatches = async (): Promise => { +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 => { } }; +/** + * 获取搜索历史记录的异步函数 + * @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; + } +} diff --git a/src/static/list/icon-input-logo.svg b/src/static/list/icon-input-logo.svg new file mode 100644 index 0000000..286df7f --- /dev/null +++ b/src/static/list/icon-input-logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/static/search/icon-back.svg b/src/static/search/icon-back.svg new file mode 100644 index 0000000..2ac5245 --- /dev/null +++ b/src/static/search/icon-back.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/static/search/icon-clear-history.svg b/src/static/search/icon-clear-history.svg new file mode 100644 index 0000000..9ab1407 --- /dev/null +++ b/src/static/search/icon-clear-history.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/static/search/icon-search-clear.svg b/src/static/search/icon-search-clear.svg new file mode 100644 index 0000000..3a7f384 --- /dev/null +++ b/src/static/search/icon-search-clear.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/static/search/icon-search-suggestion.svg b/src/static/search/icon-search-suggestion.svg new file mode 100644 index 0000000..996d104 --- /dev/null +++ b/src/static/search/icon-search-suggestion.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/static/search/icon-search.svg b/src/static/search/icon-search.svg new file mode 100644 index 0000000..ada172b --- /dev/null +++ b/src/static/search/icon-search.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/store/listStore.ts b/src/store/listStore.ts index 2b767bd..8aa3be9 100644 --- a/src/store/listStore.ts +++ b/src/store/listStore.ts @@ -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()((set, get) => ({ matches: [], loading: false, error: null, - lastRefreshTime: null, + // 搜索的value + searchValue: '', // 是否展示综合筛选弹窗 isShowFilterPopup: false, // 综合筛选项 @@ -77,6 +78,19 @@ export const useListStore = create()((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()((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 }) diff --git a/types/list/types.ts b/types/list/types.ts index e9aa406..3e6f846 100644 --- a/types/list/types.ts +++ b/types/list/types.ts @@ -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 + searchHistory: string[] + suggestionList: string[] + isShowSuggestion: boolean + isShowInputCustomerNavBar: boolean } export interface ListActions { @@ -72,6 +59,9 @@ export interface ListActions { updateState: (payload: Record) => void updateFilterOptions: (payload: Record) => void clearFilterOptions: () => void + getSearchHistory: () => Promise + clearHistory: () => void + searchSuggestion: (val: string) => Promise } // 快捷筛选