feat: 将list相关的代码迁移到game_pages目录

This commit is contained in:
2025-09-12 15:36:09 +08:00
parent 859ffec852
commit 7c16f8bcce
19 changed files with 40 additions and 40 deletions

View File

@@ -259,18 +259,18 @@ function StickyButton(props) {
) {
return {
text: "球局已结束,查看其他球局",
action: navto.bind(null, "/pages/list/index"),
action: navto.bind(null, "/game_pages/list/index"),
};
}
if (waiting_start) {
return {
text: "等待开始, 查看更多球局",
action: navto.bind(null, "/pages/list/index"),
action: navto.bind(null, "/game_pages/list/index"),
};
} else if (is_substituting) {
return {
text: "候补中,查看其他球局",
action: navto.bind(null, "/pages/list/index"),
action: navto.bind(null, "/game_pages/list/index"),
};
} else if (can_pay) {
return {
@@ -1036,7 +1036,7 @@ function Index() {
const pages = Taro.getCurrentPages();
if (pages.length <= 1) {
Taro.redirectTo({
url: "/pages/list/index",
url: "/game_pages/list/index",
});
} else {
Taro.navigateBack();

View File

@@ -0,0 +1,6 @@
export default definePageConfig({
navigationBarTitleText: '',
enablePullDownRefresh: true,
backgroundTextStyle: 'dark',
navigationStyle: 'custom',
})

View File

@@ -0,0 +1,76 @@
.listPage {
background-color: #fefefe;
.listTopSearchWrapper {
padding: 0 15px;
// position: sticky;
// background: #fefefe;
// z-index: 999;
}
// .isScroll {
// border-bottom: 0.5px solid #0000000F;
// }
.listTopFilterWrapper {
display: flex;
align-items: center;
padding-top: 10px;
padding-bottom: 10px;
gap: 5px;
}
.menuFilter {
padding: 0;
}
}
.listNavWrapper {
position: relative;
}
.toggleElement {
/* 绝对定位使两个元素重叠 */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* 过渡动画设置,实现平滑切换 */
transition: opacity 0.5s ease, transform 0.5s ease;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
border-radius: 8px;
}
/* 第一个元素样式 */
.firstElement {
background-color: #4a90e2;
color: white;
}
/* 第二个元素样式 */
.secondElement {
background-color: #5cb85c;
color: white;
/* 初始状态:透明且稍微偏移 */
opacity: 0;
transform: translateY(20px);
}
/* 可见状态 */
.visible {
opacity: 1;
transform: translateY(0);
}
/* 隐藏状态 */
.hidden {
opacity: 0;
transform: translateY(20px);
pointer-events: none; /* 隐藏时不响应鼠标事件 */
}

View File

@@ -0,0 +1,230 @@
import SearchBar from "@/components/SearchBar";
import FilterPopup from "@/components/FilterPopup";
import styles from "./index.module.scss";
import { useEffect } from "react";
import Taro, { usePageScroll } from "@tarojs/taro";
import { useListStore } from "@/store/listStore";
import { useGlobalState } from "@/store/global";
import { View } from "@tarojs/components";
import CustomerNavBar from "@/container/listCustomNavbar";
import GuideBar from "@/components/GuideBar";
import ListContainer from "@/container/listContainer";
import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { withAuth } from "@/components";
import { updateUserLocation } from "@/services/userService";
// import img from "@/config/images";
// import ShareCardCanvas from "@/components/ShareCardCanvas/example";
const ListPage = () => {
// 从 store 获取数据和方法
const store = useListStore() || {};
const { statusNavbarHeightInfo, getCurrentLocationInfo } = useGlobalState() || {};
const { totalHeight } = statusNavbarHeightInfo || {};
const {
isShowFilterPopup,
error,
matches,
recommendList,
loading,
getMatchesData,
updateState,
filterCount,
updateFilterOptions, // 更新筛选条件
filterOptions,
clearFilterOptions,
distanceData,
quickFilterData,
distanceQuickFilter,
isScrollTop,
searchValue,
isShowInputCustomerNavBar,
initialFilterSearch,
loadMoreMatches,
dateRangeOptions
} = store;
usePageScroll((res) => {
if (res?.scrollTop >= totalHeight) {
!isShowInputCustomerNavBar &&
updateState({ isShowInputCustomerNavBar: true });
} else {
isShowInputCustomerNavBar &&
updateState({ isShowInputCustomerNavBar: false });
}
});
useEffect(() => {
getLocation()
}, []);
// 监听距离和排序方式变化,自动调用接口
useEffect(() => {
// 只有当 distanceQuickFilter 有值时才调用接口
if (distanceQuickFilter?.distance !== undefined || distanceQuickFilter?.quick !== undefined) {
if (distanceQuickFilter?.quick !== "0") {
getMatchesData();
}
}
}, [distanceQuickFilter?.distance, distanceQuickFilter?.quick]);
// 获取位置信息
const getLocation = async () => {
const location = await getCurrentLocationInfo()
// 保存位置到全局状态
updateState({ location });
// 同时更新用户位置到后端和 store
if (location && location.latitude && location.longitude) {
try {
await updateUserLocation(location.latitude, location.longitude);
} catch (error) {
console.error('更新用户位置失败:', error);
}
}
// 页面加载时获取数据
getMatchesData();
return location;
}
const refreshMatches = () => {
initialFilterSearch();
};
// const getLoadMoreMatches = () => {
// loadMoreMatches()
// }
// 下拉刷新处理函数 - 使用Taro生命周期钩子
Taro.usePullDownRefresh(() => {
console.log("===触发下拉刷新");
clearFilterOptions()
// 调用 store 的刷新方法
// refreshMatches()
// .then(() => {
// // 刷新完成后停止下拉刷新动画
// Taro.stopPullDownRefresh();
// // 显示刷新成功提示
// Taro.showToast({
// title: "刷新成功",
// icon: "success",
// duration: 1500,
// });
// })
// .catch(() => {
// // 刷新失败时也停止动画
// Taro.stopPullDownRefresh();
// Taro.showToast({
// title: "刷新失败",
// icon: "error",
// duration: 1500,
// });
// });
});
const toggleShowPopup = () => {
updateState({ isShowFilterPopup: !isShowFilterPopup });
};
/**
* @description 更新筛选条件
* @param {Record<string, any>} params 筛选项
*/
const handleUpdateFilterOptions = (params: Record<string, any>) => {
updateFilterOptions(params);
};
const handleSearchChange = () => { };
// 距离筛选
const handleDistanceOrQuickChange = (name, value) => {
updateState({
distanceQuickFilter: {
...distanceQuickFilter,
[name]: value,
},
});
};
const handleSearchClick = () => {
Taro.navigateTo({
url: "/game_pages/search/index",
});
};
return (
<View>
{/* 自定义导航 */}
<CustomerNavBar />
{/* <ShareCardCanvas /> */}
<View className={styles.listPage}>
<View
className={`${styles.listTopSearchWrapper} ${isScrollTop ? styles.isScroll : ""
}`}
>
<SearchBar
handleFilterIcon={toggleShowPopup}
isSelect={filterCount > 0}
filterCount={filterCount}
onChange={handleSearchChange}
value={searchValue}
onInputClick={handleSearchClick}
/>
{/* 综合筛选 */}
{isShowFilterPopup && (
<View>
<FilterPopup
loading={loading}
onCancel={toggleShowPopup}
onConfirm={toggleShowPopup}
onChange={handleUpdateFilterOptions}
filterOptions={filterOptions}
onClear={clearFilterOptions}
visible={isShowFilterPopup}
onClose={toggleShowPopup}
statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight}
/>
</View>
)}
</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}
loadMoreMatches={loadMoreMatches}
/>
</View>
<GuideBar currentPage="list" />
</View>
);
};
export default withAuth(ListPage);

View File

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

View File

@@ -0,0 +1,121 @@
.listSearchContainer {
padding: 0 15px;
padding-top: 16px;
.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;
}
}
}
}

View File

@@ -0,0 +1,208 @@
// 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 { withAuth } from "@/components";
import "./index.scss";
import Taro from "@tarojs/taro";
const ListSearch = () => {
const {
searchValue,
updateState,
getSearchHistory,
searchHistory = [],
clearHistory,
searchSuggestion,
suggestionList,
isShowSuggestion,
} = useListState() || {};
const ref = useRef<any>(null);
useEffect(() => {
getSearchHistory();
return () => {
handleClear();
};
}, []);
useEffect(() => {
if (ref?.current) {
ref.current.focus();
}
}, [ref.current]);
const regex = useMemo(() => {
return new RegExp(searchValue, "gi");
}, [searchValue]);
// 是否显示清空图标
const isShowClearIcon = searchValue && searchValue?.length > 0;
// 是否显示搜索历史
const isShowHistory = searchHistory && searchHistory?.length > 0;
/**
* @description 输入
* @param value
*/
const handleChange = (value: string) => {
updateState({ searchValue: value });
if (value) {
searchSuggestion(value);
} else {
updateState({
isShowSuggestion: false,
});
}
};
/**
* @description 点击清空输入内容
*/
const handleClear = () => {
updateState({ searchValue: "", isShowSuggestion: false });
};
/**
* @description 点击历史搜索
* @param value
*/
const handleHistoryClick = (item: { id: number; title: string }) => {
updateState({ searchValue: item?.title });
handleSearch(item?.title);
};
/**
* @description 清空历史搜索
*/
const handleClearHistory = () => {
clearHistory();
};
/**
* @description 点击联想词
*/
const handleSuggestionSearch = (val: string) => {
updateState({
searchValue: val,
isShowSuggestion: false,
});
handleSearch(val);
};
/**
* @description 点击搜索
*/
const handleSearch = (val?: string) => {
if (!val) {
return;
}
Taro.navigateTo({
url: `/game_pages/searchResult/index`,
});
};
return (
<>
<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(searchValue)}>
</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) => {
if (!item?.title) {
return null;
}
return (
<Text
className="historySearchItem"
onClick={() => handleHistoryClick(item)}
>
{item?.title}
</Text>
);
})}
</View>
)}
</View>
)}
</View>
</>
);
};
export default withAuth(ListSearch);

View File

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

View File

@@ -0,0 +1,68 @@
.searchResultPage {
position: relative;
.searchResultFilterWrapper {
position: sticky;
display: flex;
align-items: center;
gap: 5px;
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 {
padding: 0;
}
}

View File

@@ -0,0 +1,157 @@
import { View, Image, Text } from "@tarojs/components";
// import { useSearchResultState } from "@/store/searchResultStore";
import { useListStore } from "@/store/listStore";
import { useGlobalState } from "@/store/global";
import ListContainer from "@/container/listContainer";
import img from "@/config/images";
import CustomerNavBar from "@/container/listCustomNavbar";
import DistanceQuickFilter from "@/components/DistanceQuickFilter";
import { withAuth } from "@/components";
import { useEffect } from "react";
import FilterPopup from "@/components/FilterPopup";
import "./index.scss";
import Taro from "@tarojs/taro";
const SearchResult = () => {
const {
isShowFilterPopup,
error,
distanceQuickFilter,
searchResultData,
recommendList,
loading,
updateState,
filterCount,
updateFilterOptions, // 更新筛选条件
filterOptions,
clearFilterOptions,
distanceData,
quickFilterData,
loadMoreMatches,
getMatchesData
} = useListStore() || {};
const { statusNavbarHeightInfo } = useGlobalState() || {};
const { totalHeight } = statusNavbarHeightInfo || {};
const isSelect = filterCount > 0;
useEffect(() => {
const pages = Taro.getCurrentPages()
const currentPage = pages?.[pages.length - 1];
updateState({currentPage, isSearchResult: true})
// if (location?.address) {
// 保存位置
// updateState({ location });
// 页面加载时获取数据
console.log('===搜索结果页===')
getMatchesData();
// }
return () => {
console.log('===搜索结果组件卸载')
updateState({currentPage: '', isSearchResult: false})
}
}, []);
/**
* @description 更新筛选条件
* @param {Record<string, any>} params 筛选项
*/
const handleUpdateFilterOptions = (params: Record<string, any>) => {
updateFilterOptions(params);
};
const toggleShowPopup = () => {
updateState({ isShowFilterPopup: !isShowFilterPopup });
};
// 距离筛选
const handleDistanceOrQuickChange = (name, value) => {
updateState({
distanceQuickFilter: {
...distanceQuickFilter,
[name]: value,
},
});
};
const refreshMatches = () => {
getMatchesData();
};
const handleLeftIconClick = () => {
Taro.navigateBack()
}
return (
<View className="searchResultPage">
{/* 自定义导航 */}
<CustomerNavBar
config={{
showInput: true,
inputLeftIcon: img.ICON_LIST_SEARCH_BACK,
leftIconClick: handleLeftIconClick
}}
/>
{/* 筛选 */}
<View
className="searchResultFilterWrapper"
style={
{
top: `${totalHeight}px`
}
}
>
<DistanceQuickFilter
cityOptions={distanceData}
quickOptions={quickFilterData}
onChange={handleDistanceOrQuickChange}
cityName="distance"
quickName="quick"
cityValue={distanceQuickFilter?.distance}
quickValue={distanceQuickFilter?.quick}
/>
{/* 筛选 icon */}
<View
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}
statusNavbarHeigh={statusNavbarHeightInfo?.totalHeight}
/>
</View>
)}
</View>
{/* 列表内容 */}
<ListContainer
data={searchResultData}
recommendList={recommendList}
loading={loading}
error={error}
reload={refreshMatches}
loadMoreMatches={loadMoreMatches}
/>
</View>
);
};
export default withAuth(SearchResult);