1
This commit is contained in:
@@ -85,6 +85,10 @@
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #3c3c43;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap:4px;
|
||||
}
|
||||
|
||||
.distanceWrap {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
import { Menu } from "@nutui/nutui-react-taro";
|
||||
import { Image, View, ScrollView } from "@tarojs/components";
|
||||
import Taro from "@tarojs/taro";
|
||||
import img from "@/config/images";
|
||||
import Bubble from "../Bubble";
|
||||
import { useListState } from "@/store/listStore";
|
||||
import { useListState, useListStore } from "@/store/listStore";
|
||||
import { getCurrentLocation } from "@/utils/locationUtils";
|
||||
import { updateUserLocation } from "@/services/userService";
|
||||
import { useGlobalState } from "@/store/global";
|
||||
import { useUserActions } from "@/store/userStore";
|
||||
import "./index.scss";
|
||||
|
||||
const DistanceQuickFilterV2 = (props) => {
|
||||
@@ -19,15 +24,19 @@ const DistanceQuickFilterV2 = (props) => {
|
||||
quickValue,
|
||||
districtValue, // 新增:行政区选中值
|
||||
onMenuVisibleChange, // 菜单展开/收起回调
|
||||
onRelocate, // 重新定位回调
|
||||
} = props;
|
||||
const cityRef = useRef(null);
|
||||
const quickRef = useRef(null);
|
||||
const [changePosition, setChangePosition] = useState<number[]>([]);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [keys, setKeys] = useState(0);
|
||||
const [keys, setKeys] = useState(0);
|
||||
const [isRelocating, setIsRelocating] = useState(false);
|
||||
// 从 store 获取当前城市信息
|
||||
const { area } = useListState();
|
||||
const currentCity = area?.at(-1) || ""; // 获取省份/城市名称
|
||||
const { updateState } = useGlobalState() || {};
|
||||
const { fetchUserInfo, updateCache } = useUserActions();
|
||||
|
||||
// 全城筛选显示的标题 - 如果选择了行政区,显示行政区名称
|
||||
const getCityTitle = () => {
|
||||
@@ -79,6 +88,64 @@ const DistanceQuickFilterV2 = (props) => {
|
||||
index === 1 && (quickRef.current as any)?.toggle(false);
|
||||
};
|
||||
|
||||
|
||||
// 重新获取当前位置,调用接口把位置传递后端
|
||||
const handleRelocate = async () => {
|
||||
if (isRelocating) return;
|
||||
|
||||
setIsRelocating(true);
|
||||
(Taro as any).showLoading({ title: '定位中...', mask: true });
|
||||
|
||||
try {
|
||||
// 获取当前位置
|
||||
const location = await getCurrentLocation();
|
||||
|
||||
if (location && location.latitude && location.longitude) {
|
||||
// 更新 store 中的位置信息
|
||||
updateState?.({ location });
|
||||
|
||||
// 调用接口把位置传递给后端,传递一个值代表强制更新
|
||||
const response = await updateUserLocation(location.latitude, location.longitude, true);
|
||||
|
||||
// 如果接口返回成功,重新调用用户信息接口来更新 USER_SELECTED_CITY
|
||||
if (response?.code === 0) {
|
||||
|
||||
// 删除 缓存
|
||||
(Taro as any).removeStorageSync("USER_SELECTED_CITY");
|
||||
|
||||
// 延时一下
|
||||
await new Promise(resolve => setTimeout(resolve, 600));
|
||||
// 先清除缓存和 area,确保使用最新的用户信息
|
||||
await updateCache( ["中国", response.data.last_location_province]);
|
||||
|
||||
}
|
||||
|
||||
(Taro as any).showToast({
|
||||
title: '定位成功',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
|
||||
// 通知父组件位置已更新,可以刷新列表
|
||||
if (onRelocate) {
|
||||
onRelocate(location);
|
||||
}
|
||||
} else {
|
||||
throw new Error('获取位置信息失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('重新定位失败:', error);
|
||||
(Taro as any).showToast({
|
||||
title: error?.message || '定位失败,请检查定位权限',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
} finally {
|
||||
setIsRelocating(false);
|
||||
(Taro as any).hideLoading();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听菜单状态变化,通知父组件
|
||||
useEffect(() => {
|
||||
onMenuVisibleChange?.(isMenuOpen);
|
||||
@@ -103,8 +170,11 @@ const DistanceQuickFilterV2 = (props) => {
|
||||
icon={<Image src={img.ICON_MENU_ITEM_SELECTED} />}
|
||||
>
|
||||
<div className="positionWrap">
|
||||
<p className="title">当前位置</p>
|
||||
<p className="cityName">{currentCity}</p>
|
||||
<p className="title">{currentCity}</p>
|
||||
<p className="cityName" onClick={handleRelocate}>
|
||||
<img src={img.ICON_RELOCATE} style={{ width: '12px', height: "12px" }} />
|
||||
<span>重新定位</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="distanceWrap">
|
||||
<Bubble
|
||||
|
||||
@@ -67,7 +67,7 @@ const PublishMenu: React.FC<PublishMenuProps> = (props) => {
|
||||
};
|
||||
const handleMenuItemClick = (type: "individual" | "group" | "ai") => {
|
||||
const [_, address] = area;
|
||||
if (address !== '上海') {
|
||||
if (address !== '上海市') {
|
||||
(Taro as any).showModal({
|
||||
title: '提示',
|
||||
content: '仅上海地区开放,您可加入社群或切换城市',
|
||||
|
||||
@@ -45,7 +45,7 @@ const SearchBarComponent = (props: IProps) => {
|
||||
</View>
|
||||
}
|
||||
className={styles.searchBar}
|
||||
placeholder="搜索上海的球局和场地"
|
||||
placeholder="搜索球局和场地"
|
||||
onChange={handleChange}
|
||||
value={value}
|
||||
onInputClick={onInputClick}
|
||||
|
||||
@@ -70,4 +70,5 @@ export default {
|
||||
ICON_LIST_NTPR: require('@/static/list/ntpr.svg'),
|
||||
ICON_LIST_CHANGDA: require('@/static/list/icon-changda.svg'),
|
||||
ICON_LIST_CHANGDA_QIuju: require('@/static/list/changdaqiuju.png'),
|
||||
ICON_RELOCATE: require('@/static/list/icon-relocate.svg'),
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ const ListSearch = () => {
|
||||
<View className="topSearch">
|
||||
<Image className="searchIcon" src={img.ICON_LIST_SEARCH_SEARCH} />
|
||||
<Input
|
||||
placeholder="搜索上海的球局和场地"
|
||||
placeholder="搜索球局和场地"
|
||||
value={searchValue}
|
||||
defaultValue={searchValue}
|
||||
onChange={handleChange}
|
||||
|
||||
@@ -62,6 +62,7 @@ function SharePoster(props) {
|
||||
const qrCodeUrl = await base64ToTempFilePath(
|
||||
qrCodeUrlRes.data.qr_code_base64
|
||||
);
|
||||
debugger
|
||||
await delay(100);
|
||||
const url = await generatePosterImage({
|
||||
playType: play_type,
|
||||
|
||||
@@ -446,6 +446,17 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
// 处理重新定位
|
||||
const handleRelocate = async (location) => {
|
||||
try {
|
||||
// 位置已更新到后端,刷新列表数据
|
||||
await getMatchesData();
|
||||
await fetchGetGamesCount();
|
||||
} catch (error) {
|
||||
console.error("刷新列表失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearchClick = () => {
|
||||
navigateTo({
|
||||
url: "/game_pages/search/index",
|
||||
@@ -508,7 +519,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
||||
|
||||
// 判定是否显示"暂无球局"页面
|
||||
// 条件:省份不是上海 或 (已加载完成且球局数量为0)
|
||||
const shouldShowNoGames = province !== "上海";
|
||||
const shouldShowNoGames = province !== "上海市";
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -559,6 +570,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
||||
quickValue={distanceQuickFilter?.order}
|
||||
districtValue={distanceQuickFilter?.district}
|
||||
onMenuVisibleChange={handleDistanceFilterVisibleChange}
|
||||
onRelocate={handleRelocate}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { UserInfo } from "@/components/UserInfo";
|
||||
import { API_CONFIG } from "@/config/api";
|
||||
import httpService, { ApiResponse } from "./httpService";
|
||||
import uploadFiles from "./uploadFiles";
|
||||
import Taro from "@tarojs/taro";
|
||||
import * as Taro from "@tarojs/taro";
|
||||
import getCurrentConfig from "@/config/env";
|
||||
import { clear_login_state } from "@/services/loginService";
|
||||
|
||||
@@ -740,12 +740,14 @@ export const updateUserProfile = async (payload: Partial<UserInfoType>) => {
|
||||
// 更新用户坐标位置
|
||||
export const updateUserLocation = async (
|
||||
latitude: number,
|
||||
longitude: number
|
||||
longitude: number,
|
||||
force: boolean = false
|
||||
) => {
|
||||
try {
|
||||
const response = await httpService.post("/user/update_location", {
|
||||
latitude,
|
||||
longitude,
|
||||
force
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
|
||||
10
src/static/list/icon-relocate.svg
Normal file
10
src/static/list/icon-relocate.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_8289_53268)">
|
||||
<path d="M10.9775 4.82657H8.04862C7.97102 4.82657 7.89658 4.7958 7.84163 4.74101C7.78668 4.68622 7.7557 4.61188 7.75547 4.53428V4.36457C7.75497 4.32525 7.76254 4.28625 7.77773 4.24998C7.79292 4.21371 7.81539 4.18094 7.84376 4.15371L8.88604 3.11143C8.50894 2.72825 8.05949 2.4238 7.56377 2.21574C7.06805 2.00768 6.53594 1.90016 5.99833 1.89943C5.1996 1.90138 4.41883 2.13662 3.75196 2.57623C3.08509 3.01584 2.56116 3.64067 2.24453 4.37397C1.92791 5.10727 1.83238 5.91708 1.9697 6.70392C2.10701 7.49077 2.47118 8.22036 3.01746 8.80307C3.56375 9.38578 4.26835 9.79622 5.0447 9.98397C5.82106 10.1717 6.63535 10.1286 7.38753 9.8599C8.13971 9.59121 8.79702 9.10864 9.2787 8.47149C9.76039 7.83435 10.0455 7.07037 10.0989 6.27343C10.1075 6.11828 10.236 5.99743 10.3912 5.99743H10.9775C11.0574 6.00001 11.1331 6.03387 11.1883 6.09171C11.2415 6.15 11.2698 6.22885 11.2638 6.30771C11.1937 7.51221 10.7124 8.6562 9.90046 9.54861C9.08847 10.441 7.99488 11.0278 6.80233 11.211C5.60978 11.3942 4.39049 11.1627 3.34808 10.5551C2.30568 9.94759 1.50328 9.00078 1.0749 7.87285C0.646012 6.74568 0.616794 5.50548 0.992127 4.35935C1.36746 3.21323 2.12463 2.23056 3.13719 1.57543C4.15001 0.920266 5.35687 0.632222 6.55641 0.759351C7.75595 0.88648 8.87562 1.42109 9.72862 2.274L10.6012 1.40143C10.6279 1.37384 10.6599 1.35189 10.6952 1.33687C10.7305 1.32186 10.7685 1.31408 10.8069 1.314H10.9766C11.0541 1.31422 11.1283 1.34509 11.183 1.39986C11.2378 1.45462 11.2687 1.52883 11.2689 1.60628V4.53428C11.2687 4.61173 11.2378 4.68595 11.183 4.74071C11.1283 4.79548 11.0549 4.82634 10.9775 4.82657Z" fill="#A6A6A6"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_8289_53268">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -731,16 +731,16 @@ export const useListStore = create<TennisStore>()((set, get) => ({
|
||||
const state = get();
|
||||
// 从 area 中获取省份,area 格式: ["中国", 省份, 城市]
|
||||
const country = "中国";
|
||||
const province = state.area?.at(1) || "上海"; // area[1] 是省份
|
||||
|
||||
const cn_city = state.area?.at(1) || "上海市"; // area[1] 是省份
|
||||
|
||||
const res = await getDistricts({
|
||||
country,
|
||||
state: province
|
||||
state: cn_city
|
||||
});
|
||||
|
||||
if (res.code === 0 && res.data) {
|
||||
const districts = res.data.map((item) => ({
|
||||
label: item.cn_city,
|
||||
label: item.cn_county,
|
||||
value: item.id.toString(),
|
||||
id: item.id,
|
||||
}));
|
||||
|
||||
@@ -36,6 +36,7 @@ const getTimeNextDate = (time: string) => {
|
||||
// 请求锁,避免重复调用
|
||||
let isFetchingLastTestResult = false;
|
||||
let isCheckingNicknameStatus = false;
|
||||
const CITY_CACHE_KEY = "USER_SELECTED_CITY";
|
||||
|
||||
export const useUser = create<UserState>()((set) => ({
|
||||
user: {},
|
||||
@@ -44,41 +45,50 @@ export const useUser = create<UserState>()((set) => ({
|
||||
const res = await fetchUserProfile();
|
||||
const userData = res.data;
|
||||
set({ user: userData });
|
||||
|
||||
|
||||
// 优先使用缓存中的城市,不使用用户信息中的位置
|
||||
// 检查是否有缓存的城市
|
||||
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) {
|
||||
const listStore = useListStore.getState();
|
||||
const currentArea = listStore.area;
|
||||
|
||||
// 只有当 area 不存在时才使用用户信息中的位置
|
||||
if (!currentArea) {
|
||||
const newArea: [string, string] = ["中国", userData.last_location_province];
|
||||
listStore.updateArea(newArea);
|
||||
// 保存到缓存
|
||||
try {
|
||||
(Taro as any).setStorageSync?.(CITY_CACHE_KEY, newArea);
|
||||
} catch (error) {
|
||||
console.error("保存城市缓存失败:", error);
|
||||
}
|
||||
useUser.getState().updateCache(newArea);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return userData;
|
||||
} catch (error) {
|
||||
console.error("获取用户信息失败:", error);
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
// 更新缓存
|
||||
updateCache: async (newArea: [string, string]) => {
|
||||
try {
|
||||
(Taro as any).setStorageSync?.(CITY_CACHE_KEY, newArea);
|
||||
} catch (error) {
|
||||
console.error("保存城市缓存失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
|
||||
try {
|
||||
// 先更新后端
|
||||
@@ -86,7 +96,7 @@ export const useUser = create<UserState>()((set) => ({
|
||||
// 然后立即更新本地状态(乐观更新)
|
||||
set((state) => {
|
||||
const newUser = { ...state.user, ...userInfo };
|
||||
|
||||
|
||||
// 当 userLastLocationProvince 更新时,同步更新 area
|
||||
if (userInfo.last_location_province) {
|
||||
const listStore = useListStore.getState();
|
||||
@@ -97,7 +107,7 @@ export const useUser = create<UserState>()((set) => ({
|
||||
listStore.updateArea(newArea);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return { user: newUser };
|
||||
});
|
||||
// 不再每次都重新获取完整用户信息,减少请求次数
|
||||
@@ -195,6 +205,7 @@ export const useNicknameChangeStatus = () =>
|
||||
export const useUserActions = () =>
|
||||
useUser((state) => ({
|
||||
fetchUserInfo: state.fetchUserInfo,
|
||||
updateCache: state.updateCache,
|
||||
updateUserInfo: state.updateUserInfo,
|
||||
checkNicknameChangeStatus: state.checkNicknameChangeStatus,
|
||||
updateNickname: state.updateNickname,
|
||||
|
||||
Reference in New Issue
Block a user