diff --git a/src/components/HomeNavbar/index.tsx b/src/components/HomeNavbar/index.tsx index 3929053..019eea6 100644 --- a/src/components/HomeNavbar/index.tsx +++ b/src/components/HomeNavbar/index.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useRef } from "react"; +import { useEffect, useState } from "react"; import { View, Text, Image } from "@tarojs/components"; import img from "@/config/images"; import { useGlobalState } from "@/store/global"; @@ -13,6 +13,12 @@ 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?: { @@ -90,7 +96,6 @@ const HomeNavbar = (props: IProps) => { detectedProvince: string; cachedCity: [string, string]; } | null>(null); - const hasShownLocationDialog = useRef(false); // 防止重复弹窗 // 监听城市选择器状态变化,通知父组件 useEffect(() => { @@ -104,41 +109,156 @@ const HomeNavbar = (props: IProps) => { // 只使用省份/直辖市,不使用城市(城市可能是区) 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. 尝试从缓存中读取上次的定位信息 + // 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("使用缓存的定位城市:", cachedCityArray); + console.log("[HomeNavbar] 使用缓存的定位城市:", cachedCityArray); updateArea(cachedCityArray); - // 如果用户详情中有位置信息且与缓存不同,弹窗询问是否切换 - if (detectedLocation && cachedCityArray[1] !== detectedLocation && !hasShownLocationDialog.current) { - hasShownLocationDialog.current = true; - showLocationConfirmDialog(detectedLocation, 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("没有缓存,使用用户详情中的位置信息:", detectedLocation); + // 只有在完全没有缓存的情况下,才使用用户详情中的位置信息 + console.log("[HomeNavbar] 没有缓存,使用用户详情中的位置信息:", detectedLocation); const newArea: [string, string] = ["中国", detectedLocation]; updateArea(newArea); // 保存定位信息到缓存 (Taro as any).setStorageSync(CITY_CACHE_KEY, newArea); } - }, [detectedLocation]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); // 空依赖数组,确保只在组件挂载时执行一次 - // 显示定位确认弹窗 - const showLocationConfirmDialog = (detectedLocation: string, cachedCity: [string, string]) => { - console.log('[LocationDialog] 准备显示定位确认弹窗,隐藏 GuideBar'); - setLocationDialogData({ detectedProvince: detectedLocation, cachedCity }); - setLocationDialogVisible(true); - // 显示弹窗时隐藏 GuideBar - setShowGuideBar(false); - console.log('[LocationDialog] setShowGuideBar(false) 已调用'); - }; + // 检查是否在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 = () => { @@ -150,6 +270,14 @@ const HomeNavbar = (props: IProps) => { 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); // 关闭弹窗 @@ -162,14 +290,23 @@ const HomeNavbar = (props: IProps) => { 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); @@ -258,12 +395,23 @@ const HomeNavbar = (props: IProps) => { // 处理城市切换(用户手动选择) const handleCityChange = async (_newArea: any) => { - // 用户手动选择的城市不保存到缓存(临时切换) - console.log("用户手动选择城市(不保存缓存):", _newArea); + // 用户手动选择的城市保存到缓存 + 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(); diff --git a/src/components/LocationConfirmDialog/index.scss b/src/components/LocationConfirmDialog/index.scss index 2b91270..d2db227 100644 --- a/src/components/LocationConfirmDialog/index.scss +++ b/src/components/LocationConfirmDialog/index.scss @@ -79,6 +79,10 @@ cursor: pointer; transition: opacity 0.2s; margin-top: 20px; + position: relative; + z-index: 1; + user-select: none; + -webkit-tap-highlight-color: transparent; &:first-child { margin-top: 0; @@ -111,6 +115,8 @@ font-size: 16px; line-height: 22px; text-align: center; + pointer-events: none; + user-select: none; &.primary { color: #ffffff; diff --git a/src/components/LocationConfirmDialog/index.tsx b/src/components/LocationConfirmDialog/index.tsx index 09424a8..7501b73 100644 --- a/src/components/LocationConfirmDialog/index.tsx +++ b/src/components/LocationConfirmDialog/index.tsx @@ -40,10 +40,22 @@ const LocationConfirmDialog: React.FC = ({ 定位显示您在{detectedCity} - + { + e.stopPropagation(); + onConfirm(); + }} + > 切换到{detectedCity} - + { + e.stopPropagation(); + onCancel(); + }} + > 继续浏览{currentCity} diff --git a/src/components/TitleTextarea/TitleTextarea.tsx b/src/components/TitleTextarea/TitleTextarea.tsx index f6a4c27..9870e55 100644 --- a/src/components/TitleTextarea/TitleTextarea.tsx +++ b/src/components/TitleTextarea/TitleTextarea.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import React, { useCallback, useState } from 'react' import { View } from '@tarojs/components' import { TextArea } from '@nutui/nutui-react-taro'; @@ -22,27 +22,49 @@ const TitleTextarea: React.FC = ({ onBlur }) => { const isOverflow = value.length > maxLength + // const [isFocused, setIsFocused] = useState(false) + + // const showPlaceholder = !isFocused && !value + const handleChange = useCallback((values) => { // if (values.length > maxLength ) { // const newValues = values.slice(0, maxLength) // onChange(newValues) // return; // } - onChange(values) - }, []) + onChange(values) + }, [onChange]) + + const handleFocus = useCallback(() => { + // setIsFocused(true) + onFocus?.() + }, [onFocus]) + + const handleBlur = useCallback(() => { + // setIsFocused(false) + onBlur?.() + }, [onBlur]) + return ( -