1
This commit is contained in:
@@ -64,9 +64,11 @@ export default defineAppConfig({
|
|||||||
],
|
],
|
||||||
|
|
||||||
"preloadRule": {
|
"preloadRule": {
|
||||||
"home_pages/index": {
|
// 移除首屏预加载,所有子包改为按需加载,提升启动速度
|
||||||
"packages": ["publish_pages", 'order_pages', 'user_pages', 'other_pages'],
|
// 如果需要,可以在 main_pages/index 加载后再预加载常用包
|
||||||
"network": "all" // wifi/all
|
"main_pages/index": {
|
||||||
|
"packages": ["publish_pages"], // 只预加载最常用的发布页面
|
||||||
|
"network": "all"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -32,9 +32,12 @@ const CancelPopup = forwardRef((props, ref) => {
|
|||||||
show: (onAct) => {
|
show: (onAct) => {
|
||||||
onFinish.current = onAct;
|
onFinish.current = onAct;
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
setTimeout(() => {
|
// 使用 requestAnimationFrame 替代 setTimeout(0),性能更好
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
inputRef.current && inputRef.current.focus();
|
inputRef.current && inputRef.current.focus();
|
||||||
}, 0);
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,15 @@ const ListPage = () => {
|
|||||||
isCityPickerVisible,
|
isCityPickerVisible,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// 使用 ref 保存最新的状态值,避免依赖项变化导致函数重新创建
|
||||||
|
const showSearchBarRef = useRef(showSearchBar);
|
||||||
|
const isShowInputCustomerNavBarRef = useRef(isShowInputCustomerNavBar);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
showSearchBarRef.current = showSearchBar;
|
||||||
|
isShowInputCustomerNavBarRef.current = isShowInputCustomerNavBar;
|
||||||
|
}, [showSearchBar, isShowInputCustomerNavBar]);
|
||||||
|
|
||||||
// ScrollView 滚动处理函数
|
// ScrollView 滚动处理函数
|
||||||
const handleScrollViewScroll = useCallback(
|
const handleScrollViewScroll = useCallback(
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
@@ -165,13 +174,17 @@ const ListPage = () => {
|
|||||||
const positionThreshold = 120; // 需要滚动到距离顶部120px
|
const positionThreshold = 120; // 需要滚动到距离顶部120px
|
||||||
const distanceThreshold = 80; // 需要连续滚动80px才触发
|
const distanceThreshold = 80; // 需要连续滚动80px才触发
|
||||||
|
|
||||||
|
// 使用 ref 获取最新值,避免依赖项变化
|
||||||
|
const currentShowSearchBar = showSearchBarRef.current;
|
||||||
|
const currentIsShowInputCustomerNavBar = isShowInputCustomerNavBarRef.current;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
newDirection === "up" &&
|
newDirection === "up" &&
|
||||||
currentScrollTop > positionThreshold &&
|
currentScrollTop > positionThreshold &&
|
||||||
totalScrollDistance > distanceThreshold
|
totalScrollDistance > distanceThreshold
|
||||||
) {
|
) {
|
||||||
// 上滑超过阈值,且连续滚动距离足够,隐藏搜索框
|
// 上滑超过阈值,且连续滚动距离足够,隐藏搜索框
|
||||||
if (showSearchBar || !isShowInputCustomerNavBar) {
|
if (currentShowSearchBar || !currentIsShowInputCustomerNavBar) {
|
||||||
setShowSearchBar(false);
|
setShowSearchBar(false);
|
||||||
updateListPageState({
|
updateListPageState({
|
||||||
isShowInputCustomerNavBar: true,
|
isShowInputCustomerNavBar: true,
|
||||||
@@ -184,7 +197,7 @@ const ListPage = () => {
|
|||||||
currentScrollTop <= positionThreshold
|
currentScrollTop <= positionThreshold
|
||||||
) {
|
) {
|
||||||
// 下滑且连续滚动距离足够,或者回到顶部附近,显示搜索框
|
// 下滑且连续滚动距离足够,或者回到顶部附近,显示搜索框
|
||||||
if (!showSearchBar || isShowInputCustomerNavBar) {
|
if (!currentShowSearchBar || currentIsShowInputCustomerNavBar) {
|
||||||
setShowSearchBar(true);
|
setShowSearchBar(true);
|
||||||
updateListPageState({
|
updateListPageState({
|
||||||
isShowInputCustomerNavBar: false,
|
isShowInputCustomerNavBar: false,
|
||||||
@@ -197,7 +210,8 @@ const ListPage = () => {
|
|||||||
lastScrollTopRef.current = currentScrollTop;
|
lastScrollTopRef.current = currentScrollTop;
|
||||||
lastScrollTimeRef.current = currentTime;
|
lastScrollTimeRef.current = currentTime;
|
||||||
},
|
},
|
||||||
[showSearchBar, isShowInputCustomerNavBar, updateListPageState]
|
[updateListPageState]
|
||||||
|
// 移除 showSearchBar 和 isShowInputCustomerNavBar 依赖,使用 ref 获取最新值
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -216,7 +230,10 @@ const ListPage = () => {
|
|||||||
isShowInputCustomerNavBar: false,
|
isShowInputCustomerNavBar: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [matches, pageOption?.page, updateListPageState]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [matches?.length, pageOption?.page]);
|
||||||
|
// 注意:updateListPageState 是稳定的函数引用,不需要加入依赖项
|
||||||
|
// 只依赖实际会变化的数据:matches 的长度和 pageOption.page
|
||||||
|
|
||||||
// 清理定时器
|
// 清理定时器
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -280,10 +297,10 @@ const ListPage = () => {
|
|||||||
duration: 1000,
|
duration: 1000,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
// 使用 setTimeout 确保状态更新在下一个事件循环中执行,让 ScrollView 能正确响应
|
// 使用 requestAnimationFrame 替代 setTimeout(0),性能更好
|
||||||
setTimeout(() => {
|
requestAnimationFrame(() => {
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
}, 0);
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,15 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
}
|
}
|
||||||
}, [scrollToTopTrigger, scrollToTopInternal]);
|
}, [scrollToTopTrigger, scrollToTopInternal]);
|
||||||
|
|
||||||
|
// 使用 ref 保存最新的状态值,避免依赖项变化导致函数重新创建
|
||||||
|
const showSearchBarRef = useRef(showSearchBar);
|
||||||
|
const isShowInputCustomerNavBarRef = useRef(isShowInputCustomerNavBar);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
showSearchBarRef.current = showSearchBar;
|
||||||
|
isShowInputCustomerNavBarRef.current = isShowInputCustomerNavBar;
|
||||||
|
}, [showSearchBar, isShowInputCustomerNavBar]);
|
||||||
|
|
||||||
// ScrollView 滚动处理
|
// ScrollView 滚动处理
|
||||||
const handleScrollViewScroll = useCallback(
|
const handleScrollViewScroll = useCallback(
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
@@ -143,12 +152,16 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
const positionThreshold = 120;
|
const positionThreshold = 120;
|
||||||
const distanceThreshold = 80;
|
const distanceThreshold = 80;
|
||||||
|
|
||||||
|
// 使用 ref 获取最新值,避免依赖项变化
|
||||||
|
const currentShowSearchBar = showSearchBarRef.current;
|
||||||
|
const currentIsShowInputCustomerNavBar = isShowInputCustomerNavBarRef.current;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
newDirection === "up" &&
|
newDirection === "up" &&
|
||||||
currentScrollTop > positionThreshold &&
|
currentScrollTop > positionThreshold &&
|
||||||
totalScrollDistance > distanceThreshold
|
totalScrollDistance > distanceThreshold
|
||||||
) {
|
) {
|
||||||
if (showSearchBar || !isShowInputCustomerNavBar) {
|
if (currentShowSearchBar || !currentIsShowInputCustomerNavBar) {
|
||||||
setShowSearchBar(false);
|
setShowSearchBar(false);
|
||||||
updateListPageState({
|
updateListPageState({
|
||||||
isShowInputCustomerNavBar: true,
|
isShowInputCustomerNavBar: true,
|
||||||
@@ -160,7 +173,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
(newDirection === "down" && totalScrollDistance > distanceThreshold) ||
|
(newDirection === "down" && totalScrollDistance > distanceThreshold) ||
|
||||||
currentScrollTop <= positionThreshold
|
currentScrollTop <= positionThreshold
|
||||||
) {
|
) {
|
||||||
if (!showSearchBar || isShowInputCustomerNavBar) {
|
if (!currentShowSearchBar || currentIsShowInputCustomerNavBar) {
|
||||||
setShowSearchBar(true);
|
setShowSearchBar(true);
|
||||||
updateListPageState({
|
updateListPageState({
|
||||||
isShowInputCustomerNavBar: false,
|
isShowInputCustomerNavBar: false,
|
||||||
@@ -173,12 +186,8 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
lastScrollTopRef.current = currentScrollTop;
|
lastScrollTopRef.current = currentScrollTop;
|
||||||
lastScrollTimeRef.current = currentTime;
|
lastScrollTimeRef.current = currentTime;
|
||||||
},
|
},
|
||||||
[
|
[updateListPageState, onNavStateChange]
|
||||||
showSearchBar,
|
// 移除 showSearchBar 和 isShowInputCustomerNavBar 依赖,使用 ref 获取最新值
|
||||||
isShowInputCustomerNavBar,
|
|
||||||
updateListPageState,
|
|
||||||
onNavStateChange,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -196,7 +205,10 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
});
|
});
|
||||||
onNavStateChange?.({ isShowInputCustomerNavBar: false });
|
onNavStateChange?.({ isShowInputCustomerNavBar: false });
|
||||||
}
|
}
|
||||||
}, [matches, pageOption?.page, updateListPageState, onNavStateChange]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [matches?.length, pageOption?.page]);
|
||||||
|
// 注意:updateListPageState 和 onNavStateChange 是稳定的函数引用,不需要加入依赖项
|
||||||
|
// 只依赖实际会变化的数据:matches 的长度和 pageOption.page
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
@@ -236,9 +248,10 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
duration: 1000,
|
duration: 1000,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => {
|
// 使用 requestAnimationFrame 替代 setTimeout(0),性能更好
|
||||||
|
requestAnimationFrame(() => {
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
}, 0);
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -252,13 +265,16 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
|
|||||||
// 先通知父组件筛选弹窗状态变化(设置 z-index)
|
// 先通知父组件筛选弹窗状态变化(设置 z-index)
|
||||||
onFilterPopupVisibleChange?.(newVisible);
|
onFilterPopupVisibleChange?.(newVisible);
|
||||||
// 然后更新本地状态显示/隐藏弹窗
|
// 然后更新本地状态显示/隐藏弹窗
|
||||||
// 使用 setTimeout 确保 z-index 先设置,再显示弹窗
|
// 使用 requestAnimationFrame 确保 z-index 先设置,再显示弹窗
|
||||||
if (newVisible) {
|
if (newVisible) {
|
||||||
setTimeout(() => {
|
// 使用双帧延迟确保 z-index 已生效
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
updateListPageState({
|
updateListPageState({
|
||||||
isShowFilterPopup: newVisible,
|
isShowFilterPopup: newVisible,
|
||||||
});
|
});
|
||||||
}, 50);
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// 关闭时直接更新状态
|
// 关闭时直接更新状态
|
||||||
updateListPageState({
|
updateListPageState({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { View, Text, Image, ScrollView } from "@tarojs/components";
|
import { View, Text, Image, ScrollView } from "@tarojs/components";
|
||||||
import Taro from "@tarojs/taro";
|
import Taro from "@tarojs/taro";
|
||||||
import "@/user_pages/myself/index.scss";
|
import "@/user_pages/myself/index.scss";
|
||||||
@@ -40,13 +40,8 @@ const MyselfPageContent: React.FC = () => {
|
|||||||
// 确保从编辑页面返回时刷新数据
|
// 确保从编辑页面返回时刷新数据
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
// 分类球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
|
||||||
if (!loading) {
|
const classifyGameRecords = useCallback((
|
||||||
load_game_data();
|
|
||||||
}
|
|
||||||
}, [active_tab]);
|
|
||||||
|
|
||||||
const classifyGameRecords = (
|
|
||||||
game_records: TennisMatch[]
|
game_records: TennisMatch[]
|
||||||
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
|
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
@@ -64,9 +59,10 @@ const MyselfPageContent: React.FC = () => {
|
|||||||
finishedGames: [] as TennisMatch[],
|
finishedGames: [] as TennisMatch[],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const load_game_data = async () => {
|
// 使用 useCallback 包装 load_game_data,避免每次渲染都创建新函数
|
||||||
|
const load_game_data = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
if (!user_info || !("id" in user_info)) {
|
if (!user_info || !("id" in user_info)) {
|
||||||
return;
|
return;
|
||||||
@@ -89,7 +85,13 @@ const MyselfPageContent: React.FC = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载球局数据失败:", error);
|
console.error("加载球局数据失败:", error);
|
||||||
}
|
}
|
||||||
};
|
}, [active_tab, user_info, classifyGameRecords]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading) {
|
||||||
|
load_game_data();
|
||||||
|
}
|
||||||
|
}, [loading, load_game_data]);
|
||||||
|
|
||||||
const handle_follow = async () => {
|
const handle_follow = async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ const MainPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
init();
|
init();
|
||||||
}, [fetchUserInfo]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []); // fetchUserInfo 是稳定的函数引用,不需要加入依赖项
|
||||||
|
|
||||||
// 处理标签切换
|
// 处理标签切换
|
||||||
const handleTabChange = useCallback((code: string) => {
|
const handleTabChange = useCallback((code: string) => {
|
||||||
|
|||||||
@@ -27,13 +27,31 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
|
|||||||
setKeyboardHeight: (height: number) => {
|
setKeyboardHeight: (height: number) => {
|
||||||
set({ keyboardHeight: height })
|
set({ keyboardHeight: height })
|
||||||
const { listeners } = get()
|
const { listeners } = get()
|
||||||
listeners.forEach(listener => listener(height, get().isKeyboardVisible))
|
// 使用 requestAnimationFrame 异步执行监听器,避免阻塞主线程
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
listeners.forEach(listener => {
|
||||||
|
try {
|
||||||
|
listener(height, get().isKeyboardVisible)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('键盘监听器执行错误:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
setKeyboardVisible: (visible: boolean) => {
|
setKeyboardVisible: (visible: boolean) => {
|
||||||
set({ isKeyboardVisible: visible })
|
set({ isKeyboardVisible: visible })
|
||||||
const { listeners } = get()
|
const { listeners } = get()
|
||||||
listeners.forEach(listener => listener(get().keyboardHeight, visible))
|
// 使用 requestAnimationFrame 异步执行监听器,避免阻塞主线程
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
listeners.forEach(listener => {
|
||||||
|
try {
|
||||||
|
listener(get().keyboardHeight, visible)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('键盘监听器执行错误:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
addListener: (listener: (height: number, visible: boolean) => void) => {
|
addListener: (listener: (height: number, visible: boolean) => void) => {
|
||||||
@@ -52,10 +70,27 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
|
|||||||
|
|
||||||
console.log('初始化全局键盘监听器')
|
console.log('初始化全局键盘监听器')
|
||||||
|
|
||||||
|
// 使用防抖优化,避免频繁触发
|
||||||
|
let lastHeight = 0
|
||||||
|
let lastTime = 0
|
||||||
|
const debounceDelay = 16 // 约一帧的时间
|
||||||
|
|
||||||
Taro.onKeyboardHeightChange?.((res: any) => {
|
Taro.onKeyboardHeightChange?.((res: any) => {
|
||||||
const height = Number(res?.height || 0)
|
const height = Number(res?.height || 0)
|
||||||
|
const now = Date.now()
|
||||||
|
|
||||||
|
// 防抖:如果高度没变化或时间间隔太短,忽略
|
||||||
|
if (height === lastHeight && (now - lastTime) < debounceDelay) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHeight = height
|
||||||
|
lastTime = now
|
||||||
|
|
||||||
console.log('全局键盘高度变化:', height)
|
console.log('全局键盘高度变化:', height)
|
||||||
|
|
||||||
|
// 使用 requestAnimationFrame 延迟执行,避免阻塞消息处理
|
||||||
|
requestAnimationFrame(() => {
|
||||||
const store = get()
|
const store = get()
|
||||||
if (height > 0) {
|
if (height > 0) {
|
||||||
store.setKeyboardVisible(true)
|
store.setKeyboardVisible(true)
|
||||||
@@ -65,6 +100,7 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
|
|||||||
store.setKeyboardHeight(0)
|
store.setKeyboardHeight(0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
set({ isInitialized: true })
|
set({ isInitialized: true })
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
import { View, Text, Image, ScrollView } from "@tarojs/components";
|
import { View, Text, Image, ScrollView } from "@tarojs/components";
|
||||||
import Taro, { useDidShow } from "@tarojs/taro";
|
import Taro, { useDidShow } from "@tarojs/taro";
|
||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
@@ -79,14 +79,8 @@ const MyselfPage: React.FC = () => {
|
|||||||
// set_user_info(useUserInfo()); // 确保从编辑页面返回时刷新数据
|
// set_user_info(useUserInfo()); // 确保从编辑页面返回时刷新数据
|
||||||
});
|
});
|
||||||
|
|
||||||
// 切换标签页时重新加载球局数据
|
// 分类球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
|
||||||
useEffect(() => {
|
const classifyGameRecords = useCallback((
|
||||||
if (!loading) {
|
|
||||||
load_game_data();
|
|
||||||
}
|
|
||||||
}, [active_tab]);
|
|
||||||
// 分类球局数据
|
|
||||||
const classifyGameRecords = (
|
|
||||||
game_records: TennisMatch[]
|
game_records: TennisMatch[]
|
||||||
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
|
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
@@ -104,9 +98,10 @@ const MyselfPage: React.FC = () => {
|
|||||||
finishedGames: [] as TennisMatch[],
|
finishedGames: [] as TennisMatch[],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
}, []);
|
||||||
// 加载球局数据
|
|
||||||
const load_game_data = async () => {
|
// 加载球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
|
||||||
|
const load_game_data = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
let games_data;
|
let games_data;
|
||||||
if (active_tab === "hosted") {
|
if (active_tab === "hosted") {
|
||||||
@@ -127,7 +122,14 @@ const MyselfPage: React.FC = () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载球局数据失败:", error);
|
console.error("加载球局数据失败:", error);
|
||||||
}
|
}
|
||||||
};
|
}, [active_tab, user_info, classifyGameRecords]);
|
||||||
|
|
||||||
|
// 切换标签页时重新加载球局数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading) {
|
||||||
|
load_game_data();
|
||||||
|
}
|
||||||
|
}, [loading, load_game_data]);
|
||||||
|
|
||||||
// 处理关注/取消关注
|
// 处理关注/取消关注
|
||||||
const handle_follow = async () => {
|
const handle_follow = async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user