Merge branch 'master' into feat/liujie
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { View, Text, Image } from "@tarojs/components";
|
import { View, Text, Image } from "@tarojs/components";
|
||||||
import img from "@/config/images";
|
import img from "@/config/images";
|
||||||
import { useGlobalState } from "@/store/global";
|
import { useGlobalState } from "@/store/global";
|
||||||
@@ -13,6 +13,12 @@ import LocationConfirmDialog from "@/components/LocationConfirmDialog";
|
|||||||
|
|
||||||
// 城市缓存 key
|
// 城市缓存 key
|
||||||
const CITY_CACHE_KEY = "USER_SELECTED_CITY";
|
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 {
|
interface IProps {
|
||||||
config?: {
|
config?: {
|
||||||
@@ -90,7 +96,6 @@ const HomeNavbar = (props: IProps) => {
|
|||||||
detectedProvince: string;
|
detectedProvince: string;
|
||||||
cachedCity: [string, string];
|
cachedCity: [string, string];
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const hasShownLocationDialog = useRef(false); // 防止重复弹窗
|
|
||||||
|
|
||||||
// 监听城市选择器状态变化,通知父组件
|
// 监听城市选择器状态变化,通知父组件
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -104,41 +109,156 @@ const HomeNavbar = (props: IProps) => {
|
|||||||
// 只使用省份/直辖市,不使用城市(城市可能是区)
|
// 只使用省份/直辖市,不使用城市(城市可能是区)
|
||||||
const detectedLocation = lastLocationProvince;
|
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(() => {
|
useEffect(() => {
|
||||||
// 1. 尝试从缓存中读取上次的定位信息
|
// 1. 优先尝试从缓存中读取上次的定位信息
|
||||||
const cachedCity = (Taro as any).getStorageSync(CITY_CACHE_KEY);
|
const cachedCity = (Taro as any).getStorageSync(CITY_CACHE_KEY);
|
||||||
|
|
||||||
if (cachedCity && Array.isArray(cachedCity) && cachedCity.length === 2) {
|
if (cachedCity && Array.isArray(cachedCity) && cachedCity.length === 2) {
|
||||||
// 如果有缓存的定位信息,使用缓存
|
// 如果有缓存的定位信息,使用缓存
|
||||||
const cachedCityArray = cachedCity as [string, string];
|
const cachedCityArray = cachedCity as [string, string];
|
||||||
console.log("使用缓存的定位城市:", cachedCityArray);
|
console.log("[HomeNavbar] 使用缓存的定位城市:", cachedCityArray);
|
||||||
updateArea(cachedCityArray);
|
updateArea(cachedCityArray);
|
||||||
|
|
||||||
// 如果用户详情中有位置信息且与缓存不同,弹窗询问是否切换
|
// 如果用户详情中有位置信息,且与缓存不一致,检查是否需要弹窗
|
||||||
if (detectedLocation && cachedCityArray[1] !== detectedLocation && !hasShownLocationDialog.current) {
|
if (detectedLocation && cachedCityArray[1] !== detectedLocation) {
|
||||||
hasShownLocationDialog.current = true;
|
// 检查时间缓存,如果没有或过期,则弹出选择框
|
||||||
showLocationConfirmDialog(detectedLocation, cachedCityArray);
|
if (should_show_location_dialog()) {
|
||||||
|
console.log("[HomeNavbar] 缓存城市与用户详情位置不一致,且时间过期,弹出选择框");
|
||||||
|
showLocationConfirmDialog(detectedLocation, cachedCityArray);
|
||||||
|
} else {
|
||||||
|
console.log("[HomeNavbar] 缓存城市与用户详情位置不一致,但时间未过期,不弹出选择框");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (detectedLocation) {
|
} else if (detectedLocation) {
|
||||||
// 如果没有缓存但有用户详情中的位置信息,直接使用并保存到缓存
|
// 只有在完全没有缓存的情况下,才使用用户详情中的位置信息
|
||||||
console.log("没有缓存,使用用户详情中的位置信息:", detectedLocation);
|
console.log("[HomeNavbar] 没有缓存,使用用户详情中的位置信息:", detectedLocation);
|
||||||
const newArea: [string, string] = ["中国", detectedLocation];
|
const newArea: [string, string] = ["中国", detectedLocation];
|
||||||
updateArea(newArea);
|
updateArea(newArea);
|
||||||
// 保存定位信息到缓存
|
// 保存定位信息到缓存
|
||||||
(Taro as any).setStorageSync(CITY_CACHE_KEY, newArea);
|
(Taro as any).setStorageSync(CITY_CACHE_KEY, newArea);
|
||||||
}
|
}
|
||||||
}, [detectedLocation]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []); // 空依赖数组,确保只在组件挂载时执行一次
|
||||||
|
|
||||||
// 显示定位确认弹窗
|
// 检查是否在2小时内已选择"继续浏览"或切换过城市(当前不使用,首页重新进入时直接使用缓存中的位置)
|
||||||
const showLocationConfirmDialog = (detectedLocation: string, cachedCity: [string, string]) => {
|
// const should_show_location_dialog = (): boolean => {
|
||||||
console.log('[LocationDialog] 准备显示定位确认弹窗,隐藏 GuideBar');
|
// try {
|
||||||
setLocationDialogData({ detectedProvince: detectedLocation, cachedCity });
|
// // 检查是否在2小时内切换过城市
|
||||||
setLocationDialogVisible(true);
|
// const city_change_time = (Taro as any).getStorageSync(CITY_CHANGE_TIME_KEY);
|
||||||
// 显示弹窗时隐藏 GuideBar
|
// if (city_change_time) {
|
||||||
setShowGuideBar(false);
|
// const current_time = Date.now();
|
||||||
console.log('[LocationDialog] setShowGuideBar(false) 已调用');
|
// 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 = () => {
|
const handleLocationDialogConfirm = () => {
|
||||||
@@ -150,6 +270,14 @@ const HomeNavbar = (props: IProps) => {
|
|||||||
updateArea(newArea);
|
updateArea(newArea);
|
||||||
// 更新缓存为新的定位信息
|
// 更新缓存为新的定位信息
|
||||||
(Taro as any).setStorageSync(CITY_CACHE_KEY, 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);
|
console.log("切换到用户详情中的位置信息并更新缓存:", detectedProvince);
|
||||||
|
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
@@ -162,14 +290,23 @@ const HomeNavbar = (props: IProps) => {
|
|||||||
handleCityChangeWithoutCache();
|
handleCityChangeWithoutCache();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理定位弹窗取消
|
// 处理定位弹窗取消(用户选择"继续浏览")
|
||||||
const handleLocationDialogCancel = () => {
|
const handleLocationDialogCancel = () => {
|
||||||
if (!locationDialogData) return;
|
if (!locationDialogData) return;
|
||||||
|
|
||||||
const { cachedCity } = locationDialogData;
|
const { cachedCity } = locationDialogData;
|
||||||
// 用户选择"否",保持缓存的定位城市
|
// 用户选择"继续浏览",保持缓存的定位城市
|
||||||
console.log("保持缓存的定位城市:", cachedCity[1]);
|
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);
|
setLocationDialogVisible(false);
|
||||||
setLocationDialogData(null);
|
setLocationDialogData(null);
|
||||||
@@ -258,12 +395,23 @@ const HomeNavbar = (props: IProps) => {
|
|||||||
|
|
||||||
// 处理城市切换(用户手动选择)
|
// 处理城市切换(用户手动选择)
|
||||||
const handleCityChange = async (_newArea: any) => {
|
const handleCityChange = async (_newArea: any) => {
|
||||||
// 用户手动选择的城市不保存到缓存(临时切换)
|
// 用户手动选择的城市保存到缓存
|
||||||
console.log("用户手动选择城市(不保存缓存):", _newArea);
|
console.log("用户手动选择城市,更新缓存:", _newArea);
|
||||||
|
|
||||||
// 先更新 area 状态(用于界面显示和接口参数)
|
// 先更新 area 状态(用于界面显示和接口参数)
|
||||||
updateArea(_newArea);
|
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)
|
// 先调用列表接口(会使用更新后的 state.area)
|
||||||
if (refreshBothLists) {
|
if (refreshBothLists) {
|
||||||
await refreshBothLists();
|
await refreshBothLists();
|
||||||
|
|||||||
@@ -79,6 +79,10 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
@@ -111,6 +115,8 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
&.primary {
|
&.primary {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
|||||||
@@ -40,10 +40,22 @@ const LocationConfirmDialog: React.FC<LocationConfirmDialogProps> = ({
|
|||||||
<View className="locationDialogContent">
|
<View className="locationDialogContent">
|
||||||
<Text className="locationDialogTitle">定位显示您在{detectedCity}</Text>
|
<Text className="locationDialogTitle">定位显示您在{detectedCity}</Text>
|
||||||
<View className="locationDialogButtons">
|
<View className="locationDialogButtons">
|
||||||
<View className="locationDialogButton primary" onClick={onConfirm}>
|
<View
|
||||||
|
className="locationDialogButton primary"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onConfirm();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text className="locationDialogButtonText primary">切换到{detectedCity}</Text>
|
<Text className="locationDialogButtonText primary">切换到{detectedCity}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="locationDialogButton secondary" onClick={onCancel}>
|
<View
|
||||||
|
className="locationDialogButton secondary"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onCancel();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Text className="locationDialogButtonText secondary">继续浏览{currentCity}</Text>
|
<Text className="locationDialogButtonText secondary">继续浏览{currentCity}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { View } from '@tarojs/components'
|
import { View } from '@tarojs/components'
|
||||||
import { TextArea } from '@nutui/nutui-react-taro';
|
import { TextArea } from '@nutui/nutui-react-taro';
|
||||||
|
|
||||||
@@ -22,27 +22,49 @@ const TitleTextarea: React.FC<TitleTextareaProps> = ({
|
|||||||
onBlur
|
onBlur
|
||||||
}) => {
|
}) => {
|
||||||
const isOverflow = value.length > maxLength
|
const isOverflow = value.length > maxLength
|
||||||
|
// const [isFocused, setIsFocused] = useState(false)
|
||||||
|
|
||||||
|
// const showPlaceholder = !isFocused && !value
|
||||||
|
|
||||||
const handleChange = useCallback((values) => {
|
const handleChange = useCallback((values) => {
|
||||||
// if (values.length > maxLength ) {
|
// if (values.length > maxLength ) {
|
||||||
// const newValues = values.slice(0, maxLength)
|
// const newValues = values.slice(0, maxLength)
|
||||||
// onChange(newValues)
|
// onChange(newValues)
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
onChange(values)
|
onChange(values)
|
||||||
}, [])
|
}, [onChange])
|
||||||
|
|
||||||
|
const handleFocus = useCallback(() => {
|
||||||
|
// setIsFocused(true)
|
||||||
|
onFocus?.()
|
||||||
|
}, [onFocus])
|
||||||
|
|
||||||
|
const handleBlur = useCallback(() => {
|
||||||
|
// setIsFocused(false)
|
||||||
|
onBlur?.()
|
||||||
|
}, [onBlur])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='title-input-wrapper'>
|
<View className='title-input-wrapper'>
|
||||||
<TextArea
|
<View className='title-input-box'>
|
||||||
className='title-input'
|
<TextArea
|
||||||
placeholder={placeholder}
|
className='title-input'
|
||||||
value={value}
|
placeholder={placeholder}
|
||||||
onInput={(e) => handleChange(e.detail.value)}
|
value={value}
|
||||||
// maxlength={maxLength}
|
onInput={(e) => handleChange(e.detail.value)}
|
||||||
autoSize={true}
|
// maxlength={maxLength}
|
||||||
placeholderClass='title-input-placeholder'
|
placeholderClass='title-input-placeholder'
|
||||||
onFocus={onFocus}
|
autoSize={true}
|
||||||
onBlur={onBlur}
|
onFocus={handleFocus}
|
||||||
/>
|
onBlur={handleBlur}
|
||||||
|
/>
|
||||||
|
{/* {showPlaceholder && (
|
||||||
|
<View className='title-input-placeholder-custom'>
|
||||||
|
{placeholder}
|
||||||
|
</View>
|
||||||
|
)} */}
|
||||||
|
</View>
|
||||||
<View className={`char-count${isOverflow ? ' char-count--error' : ''}`}>
|
<View className={`char-count${isOverflow ? ' char-count--error' : ''}`}>
|
||||||
{value.length}/{maxLength}
|
{value.length}/{maxLength}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -4,9 +4,22 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 8px 12px;
|
padding: 10px 12px;
|
||||||
min-height: 36px;
|
min-height: 32px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
.title-input-box {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.title-input-placeholder {
|
||||||
|
color: rgba(60, 60, 67, 0.60) !important;
|
||||||
|
font-weight: normal !important;
|
||||||
|
position: relative;
|
||||||
|
line-height: 22px !important;
|
||||||
|
height: 22px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
.title-input {
|
.title-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -17,22 +30,23 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
resize: none;
|
resize: none;
|
||||||
line-height: 26px;
|
line-height: 22px;
|
||||||
min-height: 26px;
|
min-height: 22px;
|
||||||
}
|
}
|
||||||
|
.title-input-placeholder-custom {
|
||||||
// 使用 placeholderClass 来控制 placeholder 样式
|
position: absolute;
|
||||||
.title-input-placeholder {
|
left: 7px;
|
||||||
color: rgba(60, 60, 67, 0.60) !important;
|
top: 50%;
|
||||||
font-size: 16px !important;
|
width: 100%;
|
||||||
font-weight: normal !important;
|
padding: 0;
|
||||||
line-height: 26px !important;
|
color: rgba(60, 60, 67, 0.60);
|
||||||
height: 26px;
|
font-size: 16px;
|
||||||
flex: 1;
|
font-weight: normal;
|
||||||
|
line-height: 22px;
|
||||||
|
pointer-events: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.char-count {
|
.char-count {
|
||||||
color: #999;
|
color: #999;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|||||||
@@ -129,15 +129,37 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
// ScrollView 滚动处理
|
// ScrollView 滚动处理
|
||||||
const handleScrollViewScroll = useCallback(
|
const handleScrollViewScroll = useCallback(
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
|
|
||||||
|
|
||||||
const currentScrollTop = e?.detail?.scrollTop || 0;
|
const currentScrollTop = e?.detail?.scrollTop || 0;
|
||||||
|
const scrollHeight = e?.detail?.scrollHeight || 0;
|
||||||
|
const clientHeight = e?.detail?.clientHeight || 0;
|
||||||
const lastScrollTop = lastScrollTopRef.current;
|
const lastScrollTop = lastScrollTopRef.current;
|
||||||
const currentTime = Date.now();
|
const currentTime = Date.now();
|
||||||
const timeDiff = currentTime - lastScrollTimeRef.current;
|
const timeDiff = currentTime - lastScrollTimeRef.current;
|
||||||
|
|
||||||
if (timeDiff < 100) return;
|
if (timeDiff < 100) return;
|
||||||
|
|
||||||
|
// 计算距离底部的距离,提前加载(距离底部600px时开始加载)
|
||||||
|
// 注意:如果 scrollHeight 或 clientHeight 不可用,则使用 lowerThreshold 触发
|
||||||
|
if (scrollHeight > 0 && clientHeight > 0) {
|
||||||
|
const distanceToBottom = scrollHeight - currentScrollTop - clientHeight;
|
||||||
|
const preloadThreshold = 600; // 提前加载阈值
|
||||||
|
|
||||||
|
// 如果距离底部小于阈值,且正在向下滚动,且有更多数据,则提前加载
|
||||||
|
if (
|
||||||
|
distanceToBottom < preloadThreshold &&
|
||||||
|
distanceToBottom > 0 &&
|
||||||
|
!loading &&
|
||||||
|
!loadingMoreRef.current &&
|
||||||
|
listPageState?.isHasMoreData &&
|
||||||
|
currentScrollTop > lastScrollTop // 向下滚动
|
||||||
|
) {
|
||||||
|
loadingMoreRef.current = true;
|
||||||
|
loadMoreMatches().finally(() => {
|
||||||
|
loadingMoreRef.current = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const scrollDiff = currentScrollTop - lastScrollTop;
|
const scrollDiff = currentScrollTop - lastScrollTop;
|
||||||
let newDirection = scrollDirectionRef.current;
|
let newDirection = scrollDirectionRef.current;
|
||||||
if (Math.abs(scrollDiff) > 15) {
|
if (Math.abs(scrollDiff) > 15) {
|
||||||
@@ -195,7 +217,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
lastScrollTopRef.current = currentScrollTop;
|
lastScrollTopRef.current = currentScrollTop;
|
||||||
lastScrollTimeRef.current = currentTime;
|
lastScrollTimeRef.current = currentTime;
|
||||||
},
|
},
|
||||||
[updateListPageState, onNavStateChange]
|
[updateListPageState, onNavStateChange, loading, loadMoreMatches, listPageState?.isHasMoreData]
|
||||||
// 移除 showSearchBar 和 isShowInputCustomerNavBar 依赖,使用 ref 获取最新值
|
// 移除 showSearchBar 和 isShowInputCustomerNavBar 依赖,使用 ref 获取最新值
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -214,6 +236,38 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isActive]);
|
}, [isActive]);
|
||||||
|
|
||||||
|
// 记录上一次的城市,用于检测城市变化
|
||||||
|
const prevAreaRef = useRef<[string, string] | null>(null);
|
||||||
|
|
||||||
|
// 监听城市变化,重新获取行政区列表并清空已选择的行政区
|
||||||
|
useEffect(() => {
|
||||||
|
if (area && area.length >= 2) {
|
||||||
|
const currentProvince = area[1];
|
||||||
|
const prevProvince = prevAreaRef.current?.[1];
|
||||||
|
|
||||||
|
// 只有当城市真正改变时才执行(避免初始化时也触发)
|
||||||
|
if (prevProvince && prevProvince !== currentProvince) {
|
||||||
|
console.log("城市改变,重新获取行政区列表:", {
|
||||||
|
prevProvince,
|
||||||
|
currentProvince,
|
||||||
|
});
|
||||||
|
// 城市改变时,重新获取行政区列表
|
||||||
|
getDistricts();
|
||||||
|
// 清空已选择的行政区,避免显示错误的行政区
|
||||||
|
const currentState = useListStore.getState();
|
||||||
|
const currentPageState = currentState.isSearchResult
|
||||||
|
? currentState.searchPageState
|
||||||
|
: currentState.listPageState;
|
||||||
|
if (currentPageState?.distanceQuickFilter?.district) {
|
||||||
|
updateDistanceQuickFilter({ district: undefined });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 更新记录的城市
|
||||||
|
prevAreaRef.current = area as [string, string];
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [area?.[1]]); // 只监听省份(area[1])的变化
|
||||||
|
|
||||||
// 当页面从非激活状态切换为激活状态时,检查城市是否变化,如果变化则重新加载数据
|
// 当页面从非激活状态切换为激活状态时,检查城市是否变化,如果变化则重新加载数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 如果从非激活状态变为激活状态(切回列表页)
|
// 如果从非激活状态变为激活状态(切回列表页)
|
||||||
@@ -518,7 +572,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
refresherEnabled={true}
|
refresherEnabled={true}
|
||||||
refresherTriggered={refreshing}
|
refresherTriggered={refreshing}
|
||||||
onRefresherRefresh={handleRefresh}
|
onRefresherRefresh={handleRefresh}
|
||||||
lowerThreshold={100}
|
lowerThreshold={600}
|
||||||
onScrollToLower={async () => {
|
onScrollToLower={async () => {
|
||||||
if (
|
if (
|
||||||
!loading &&
|
!loading &&
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
import Taro from "@tarojs/taro";
|
||||||
import {
|
import {
|
||||||
fetchUserProfile,
|
fetchUserProfile,
|
||||||
updateUserProfile,
|
updateUserProfile,
|
||||||
@@ -44,14 +45,31 @@ export const useUser = create<UserState>()((set) => ({
|
|||||||
const userData = res.data;
|
const userData = res.data;
|
||||||
set({ user: userData });
|
set({ user: userData });
|
||||||
|
|
||||||
// 当 userLastLocationProvince 更新时,同步更新 area
|
// 优先使用缓存中的城市,不使用用户信息中的位置
|
||||||
|
// 检查是否有缓存的城市
|
||||||
|
const CITY_CACHE_KEY = "USER_SELECTED_CITY";
|
||||||
|
const cachedCity = (Taro as any).getStorageSync?.(CITY_CACHE_KEY);
|
||||||
|
|
||||||
|
if (cachedCity && Array.isArray(cachedCity) && cachedCity.length === 2) {
|
||||||
|
// 如果有缓存的城市,使用缓存,不更新 area
|
||||||
|
console.log("[userStore] 检测到缓存的城市,使用缓存,不更新 area");
|
||||||
|
return userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有当没有缓存时,才使用用户信息中的位置
|
||||||
if (userData?.last_location_province) {
|
if (userData?.last_location_province) {
|
||||||
const listStore = useListStore.getState();
|
const listStore = useListStore.getState();
|
||||||
const currentArea = listStore.area;
|
const currentArea = listStore.area;
|
||||||
// 只有当 area 不存在或与 userLastLocationProvince 不一致时才更新
|
// 只有当 area 不存在时才使用用户信息中的位置
|
||||||
if (!currentArea || currentArea[1] !== userData.last_location_province) {
|
if (!currentArea) {
|
||||||
const newArea: [string, string] = ["中国", userData.last_location_province];
|
const newArea: [string, string] = ["中国", userData.last_location_province];
|
||||||
listStore.updateArea(newArea);
|
listStore.updateArea(newArea);
|
||||||
|
// 保存到缓存
|
||||||
|
try {
|
||||||
|
(Taro as any).setStorageSync?.(CITY_CACHE_KEY, newArea);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("保存城市缓存失败:", error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user