import { useEffect, useState } from "react"; import { View, Text, Image } from "@tarojs/components"; import img from "@/config/images"; import { useGlobalState } from "@/store/global"; import { useUserInfo } from "@/store/userStore"; import { useListState } from "@/store/listStore"; import { Input } from "@nutui/nutui-react-taro"; import Taro from "@tarojs/taro"; import "./index.scss"; import { getCurrentFullPath } from "@/utils"; import { CityPickerV2 as PopupPicker } from "@/components/Picker"; import LocationConfirmDialog from "@/components/LocationConfirmDialog"; // 城市缓存 key const CITY_CACHE_KEY = "USER_SELECTED_CITY"; // 定位弹窗关闭时间缓存 key(用户选择"继续浏览"时记录) const LOCATION_DIALOG_DISMISS_TIME_KEY = "LOCATION_DIALOG_DISMISS_TIME"; // 城市切换时间缓存 key(用户手动切换城市时记录) const CITY_CHANGE_TIME_KEY = "CITY_CHANGE_TIME"; // 2小时的毫秒数 const TWO_HOURS_MS = 2 * 60 * 60 * 1000; interface IProps { config?: { showInput?: boolean; inputLeftIcon?: string; iconPath?: string; leftIconClick?: () => void; title?: string; // 显示标题(用于"我的"页面等) showTitle?: boolean; // 是否显示标题模式 }; onCityPickerVisibleChange?: (visible: boolean) => void; // 城市选择器显示/隐藏回调 onScrollToTop?: () => void; // 滚动到顶部回调 } function CityPicker(props) { const { visible, setVisible, cities, area, setArea, onCityChange } = props; console.log(cities, "cities"); const [value, setValue] = useState(area); function onChange(value: any) { console.log(value, "value"); setValue(value); setArea(value); // 切换城市时触发接口调用 if (onCityChange) { onCityChange(value); } } return ( visible && ( ) ); } /** * 首页专用导航栏组件 * 支持三种模式: * 1. Logo + 城市选择 + 球局数量(默认模式) * 2. 搜索输入框模式(showInput = true) * 3. 标题模式(showTitle = true,用于"我的"页面等) */ const HomeNavbar = (props: IProps) => { const { config, onCityPickerVisibleChange, onScrollToTop } = props; const { showInput = false, inputLeftIcon, leftIconClick, title, showTitle = false, } = config || {}; const { getLocationLoading, statusNavbarHeightInfo, setShowGuideBar } = useGlobalState(); const { gamesNum, searchValue, cities, area, updateArea, fetchGetGamesCount, refreshBothLists, } = useListState(); const { statusBarHeight = 0, navBarHeight = 44 } = statusNavbarHeightInfo || {}; const [cityPopupVisible, setCityPopupVisible] = useState(false); const [locationDialogVisible, setLocationDialogVisible] = useState(false); const [locationDialogData, setLocationDialogData] = useState<{ detectedProvince: string; cachedCity: [string, string]; } | null>(null); // 监听城市选择器状态变化,通知父组件 useEffect(() => { onCityPickerVisibleChange?.(cityPopupVisible); }, [cityPopupVisible]); const userInfo = useUserInfo(); // 使用用户详情接口中的 last_location 字段 // USER_SELECTED_CITY 第二个值应该是省份/直辖市,不能是区 const lastLocationProvince = (userInfo as any)?.last_location_province || ""; // 只使用省份/直辖市,不使用城市(城市可能是区) const detectedLocation = lastLocationProvince; // 检查是否应该显示定位确认弹窗 const should_show_location_dialog = (): boolean => { try { const current_time = Date.now(); // 检查是否在2小时内切换过城市 const city_change_time = (Taro as any).getStorageSync(CITY_CHANGE_TIME_KEY); if (city_change_time) { const time_diff = current_time - city_change_time; // 如果距离上次切换城市还在2小时内,不显示弹窗 if (time_diff < TWO_HOURS_MS) { console.log(`[HomeNavbar] 距离上次切换城市还不到2小时,剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟,不显示定位弹窗`); return false; } else { // 超过2小时,清除过期记录 (Taro as any).removeStorageSync(CITY_CHANGE_TIME_KEY); } } // 检查是否在2小时内已选择"继续浏览" const dismiss_time = (Taro as any).getStorageSync(LOCATION_DIALOG_DISMISS_TIME_KEY); if (!dismiss_time) { return true; // 没有记录,可以显示 } const time_diff = current_time - dismiss_time; // 如果距离上次选择"继续浏览"已超过2小时,可以再次显示 if (time_diff >= TWO_HOURS_MS) { // 清除过期记录 (Taro as any).removeStorageSync(LOCATION_DIALOG_DISMISS_TIME_KEY); return true; } // 在2小时内,不显示弹窗 console.log(`[HomeNavbar] 距离上次选择"继续浏览"还不到2小时,剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟`); return false; } catch (error) { console.error('[HomeNavbar] 检查定位弹窗显示条件失败:', error); return true; // 出错时默认显示 } }; // 显示定位确认弹窗 const showLocationConfirmDialog = (detectedLocation: string, cachedCity: [string, string]) => { // 检查是否应该显示弹窗 if (!should_show_location_dialog()) { console.log('[HomeNavbar] 用户在2小时内已选择"继续浏览"或切换过城市,不显示弹窗'); return; } console.log('[HomeNavbar] 准备显示定位确认弹窗,隐藏 GuideBar'); setLocationDialogData({ detectedProvince: detectedLocation, cachedCity }); setLocationDialogVisible(true); // 显示弹窗时隐藏 GuideBar setShowGuideBar(false); console.log('[HomeNavbar] setShowGuideBar(false) 已调用'); }; // 初始化城市:优先使用缓存的定位信息,如果缓存城市和用户详情位置不一致,且时间过期,则弹出选择框 // 只在组件挂载时执行一次,避免重复执行 useEffect(() => { // 1. 优先尝试从缓存中读取上次的定位信息 const cachedCity = (Taro as any).getStorageSync(CITY_CACHE_KEY); if (cachedCity && Array.isArray(cachedCity) && cachedCity.length === 2) { // 如果有缓存的定位信息,使用缓存 const cachedCityArray = cachedCity as [string, string]; console.log("[HomeNavbar] 使用缓存的定位城市:", cachedCityArray); updateArea(cachedCityArray); // 如果用户详情中有位置信息,且与缓存不一致,检查是否需要弹窗 if (detectedLocation && cachedCityArray[1] !== detectedLocation) { // 检查时间缓存,如果没有或过期,则弹出选择框 if (should_show_location_dialog()) { console.log("[HomeNavbar] 缓存城市与用户详情位置不一致,且时间过期,弹出选择框"); showLocationConfirmDialog(detectedLocation, cachedCityArray); } else { console.log("[HomeNavbar] 缓存城市与用户详情位置不一致,但时间未过期,不弹出选择框"); } } } else if (detectedLocation) { // 只有在完全没有缓存的情况下,才使用用户详情中的位置信息 console.log("[HomeNavbar] 没有缓存,使用用户详情中的位置信息:", detectedLocation); const newArea: [string, string] = ["中国", detectedLocation]; updateArea(newArea); // 保存定位信息到缓存 (Taro as any).setStorageSync(CITY_CACHE_KEY, newArea); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 空依赖数组,确保只在组件挂载时执行一次 // 检查是否在2小时内已选择"继续浏览"或切换过城市(当前不使用,首页重新进入时直接使用缓存中的位置) // const should_show_location_dialog = (): boolean => { // try { // // 检查是否在2小时内切换过城市 // const city_change_time = (Taro as any).getStorageSync(CITY_CHANGE_TIME_KEY); // if (city_change_time) { // const current_time = Date.now(); // const time_diff = current_time - city_change_time; // // // 如果距离上次切换城市还在2小时内,不显示弹窗 // if (time_diff < TWO_HOURS_MS) { // console.log(`距离上次切换城市还不到2小时,剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟,不显示定位弹窗`); // return false; // } else { // // 超过2小时,清除过期记录 // (Taro as any).removeStorageSync(CITY_CHANGE_TIME_KEY); // } // } // // // 检查是否在2小时内已选择"继续浏览" // const dismiss_time = (Taro as any).getStorageSync(LOCATION_DIALOG_DISMISS_TIME_KEY); // if (!dismiss_time) { // return true; // 没有记录,可以显示 // } // // const current_time = Date.now(); // const time_diff = current_time - dismiss_time; // // // 如果距离上次选择"继续浏览"已超过2小时,可以再次显示 // if (time_diff >= TWO_HOURS_MS) { // // 清除过期记录 // (Taro as any).removeStorageSync(LOCATION_DIALOG_DISMISS_TIME_KEY); // return true; // } // // // 在2小时内,不显示弹窗 // console.log(`距离上次选择"继续浏览"还不到2小时,剩余时间: ${Math.ceil((TWO_HOURS_MS - time_diff) / 1000 / 60)}分钟`); // return false; // } catch (error) { // console.error('检查定位弹窗显示条件失败:', error); // return true; // 出错时默认显示 // } // }; // 显示定位确认弹窗(当前不使用,首页重新进入时直接使用缓存中的位置) // const showLocationConfirmDialog = (detectedLocation: string, cachedCity: [string, string]) => { // // 检查是否在2小时内已选择"继续浏览" // if (!should_show_location_dialog()) { // console.log('[LocationDialog] 用户在2小时内已选择"继续浏览",不显示弹窗'); // return; // } // // console.log('[LocationDialog] 准备显示定位确认弹窗,隐藏 GuideBar'); // setLocationDialogData({ detectedProvince: detectedLocation, cachedCity }); // setLocationDialogVisible(true); // // 显示弹窗时隐藏 GuideBar // setShowGuideBar(false); // console.log('[LocationDialog] setShowGuideBar(false) 已调用'); // }; // 处理定位弹窗确认 const handleLocationDialogConfirm = () => { if (!locationDialogData) return; const { detectedProvince } = locationDialogData; // 用户选择"切换到",使用用户详情中的位置信息 const newArea: [string, string] = ["中国", detectedProvince]; updateArea(newArea); // 更新缓存为新的定位信息 (Taro as any).setStorageSync(CITY_CACHE_KEY, newArea); // 记录切换城市的时间戳,2小时内不再提示 try { const current_time = Date.now(); (Taro as any).setStorageSync(CITY_CHANGE_TIME_KEY, current_time); console.log(`[LocationDialog] 已记录用户切换城市的时间,2小时内不再提示`); } catch (error) { console.error('保存城市切换时间失败:', error); } console.log("切换到用户详情中的位置信息并更新缓存:", detectedProvince); // 关闭弹窗 setLocationDialogVisible(false); setLocationDialogData(null); // 关闭弹窗时显示 GuideBar setShowGuideBar(true); // 刷新数据 handleCityChangeWithoutCache(); }; // 处理定位弹窗取消(用户选择"继续浏览") const handleLocationDialogCancel = () => { if (!locationDialogData) return; const { cachedCity } = locationDialogData; // 用户选择"继续浏览",保持缓存的定位城市 console.log("保持缓存的定位城市:", cachedCity[1]); // 记录用户选择"继续浏览"的时间戳,2小时内不再提示 try { const current_time = Date.now(); (Taro as any).setStorageSync(LOCATION_DIALOG_DISMISS_TIME_KEY, current_time); console.log(`[LocationDialog] 已记录用户选择"继续浏览"的时间,2小时内不再提示`); } catch (error) { console.error('保存定位弹窗关闭时间失败:', error); } // 关闭弹窗 setLocationDialogVisible(false); setLocationDialogData(null); // 关闭弹窗时显示 GuideBar setShowGuideBar(true); }; // const currentAddress = city + district; const handleInputClick = () => { // 关闭城市选择器 if (cityPopupVisible) { setCityPopupVisible(false); } const currentPagePath = getCurrentFullPath(); if (currentPagePath === "/game_pages/searchResult/index") { (Taro as any).navigateBack(); } else { (Taro as any).navigateTo({ url: "/game_pages/search/index", }); } }; // 点击logo const handleLogoClick = () => { // 关闭城市选择器 if (cityPopupVisible) { setCityPopupVisible(false); } // 如果当前在列表页,点击后页面回到顶部 if (getCurrentFullPath() === "/main_pages/index") { // 使用父组件传递的滚动方法(适配 ScrollView) if (onScrollToTop) { onScrollToTop(); } else { // 降级方案:使用页面滚动(兼容旧版本) (Taro as any).pageScrollTo({ scrollTop: 0, duration: 300, }); } return; // 已经在列表页,只滚动到顶部,不需要跳转 } (Taro as any).redirectTo({ url: "/main_pages/index", // 列表页 }); }; const handleInputLeftIconClick = () => { // 关闭城市选择器 if (cityPopupVisible) { setCityPopupVisible(false); } if (leftIconClick) { leftIconClick(); } else { handleLogoClick(); } }; const navbarStyle = { height: `${navBarHeight}px`, }; function handleToggleCity() { setCityPopupVisible(true); } const area_city = area.at(-1); // 处理城市切换(仅刷新数据,不保存缓存) const handleCityChangeWithoutCache = async () => { // 先调用列表接口 if (refreshBothLists) { await refreshBothLists(); } // 列表接口完成后,再调用数量接口 if (fetchGetGamesCount) { await fetchGetGamesCount(); } }; // 处理城市切换(用户手动选择) const handleCityChange = async (_newArea: any) => { // 用户手动选择的城市保存到缓存 console.log("用户手动选择城市,更新缓存:", _newArea); // 先更新 area 状态(用于界面显示和接口参数) updateArea(_newArea); // 保存城市到缓存 try { (Taro as any).setStorageSync(CITY_CACHE_KEY, _newArea); // 记录切换时间,2小时内不再弹出定位弹窗 const current_time = Date.now(); (Taro as any).setStorageSync(CITY_CHANGE_TIME_KEY, current_time); console.log("已保存城市到缓存并记录切换时间:", _newArea, current_time); } catch (error) { console.error("保存城市缓存失败:", error); } // 先调用列表接口(会使用更新后的 state.area) if (refreshBothLists) { await refreshBothLists(); } // 列表接口完成后,再调用数量接口(会使用更新后的 state.area) if (fetchGetGamesCount) { await fetchGetGamesCount(); } }; return ( {/* 标题模式(用于"我的"页面等) */} {showTitle && ( {title || "我的"} )} {/* 首页logo 导航*/} {!showTitle && ( {cityPopupVisible && !showTitle && ( )} {/* 定位确认弹窗 */} {locationDialogData && ( )} ); }; export default HomeNavbar;