修复分享页面不能访问问题
This commit is contained in:
@@ -51,7 +51,12 @@ const PopupPicker = ({
|
||||
}: PickerProps) => {
|
||||
const [defaultValue, setDefaultValue] = useState<(string | number)[]>([]);
|
||||
const [defaultOptions, setDefaultOptions] = useState<PickerOption[][]>([]);
|
||||
const [pickerCurrentValue, setPickerCurrentValue] = useState<(string | number)[]>(value);
|
||||
|
||||
const changePicker = (options: any[], values: any, columnIndex: number) => {
|
||||
// 更新 Picker 的当前值
|
||||
setPickerCurrentValue(values);
|
||||
|
||||
if (onChange) {
|
||||
console.log("picker onChange", columnIndex, values, options);
|
||||
if (
|
||||
@@ -77,8 +82,19 @@ const PopupPicker = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 处理 Picker 的确认事件,获取当前选中的值
|
||||
const handlePickerConfirm = (options: PickerOption[], values: (string | number)[]) => {
|
||||
setPickerCurrentValue(values);
|
||||
setDefaultValue(values);
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
onChange(defaultValue);
|
||||
// 如果用户滚动过(defaultValue 不为空),使用 defaultValue
|
||||
// 如果用户没有滚动(defaultValue 为空),使用传入的默认值(即当前显示的默认选项)
|
||||
const valueToSave = defaultValue.length > 0 ? defaultValue : value;
|
||||
if (onChange && valueToSave.length > 0) {
|
||||
onChange(valueToSave);
|
||||
}
|
||||
setvisible(false);
|
||||
};
|
||||
|
||||
@@ -97,11 +113,19 @@ const PopupPicker = ({
|
||||
}
|
||||
}, [type]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (value.length > 0 && defaultOptions.length > 0) {
|
||||
// setDefaultValue([...value])
|
||||
// }
|
||||
// }, [value, defaultOptions])
|
||||
// 当选择器打开时,初始化 defaultValue 和 pickerCurrentValue
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
if (value.length > 0) {
|
||||
setDefaultValue([...value]);
|
||||
setPickerCurrentValue([...value]);
|
||||
} else {
|
||||
// 如果 value 为空,重置为初始状态
|
||||
setDefaultValue([]);
|
||||
setPickerCurrentValue([]);
|
||||
}
|
||||
}
|
||||
}, [visible, value]);
|
||||
return (
|
||||
<>
|
||||
<CommonPopup
|
||||
@@ -147,6 +171,7 @@ const PopupPicker = ({
|
||||
options={defaultOptions}
|
||||
defaultValue={value}
|
||||
onChange={changePicker}
|
||||
onConfirm={handlePickerConfirm}
|
||||
/>
|
||||
</CommonPopup>
|
||||
</>
|
||||
|
||||
@@ -147,9 +147,17 @@ function Intro() {
|
||||
const [ready, setReady] = useState(false);
|
||||
const { setCallback } = useEvaluate();
|
||||
|
||||
const { last_test_result: { ntrp_level, create_time, id } = {} } =
|
||||
ntrpData || {};
|
||||
const lastTestTime = dayjs(create_time).format("YYYY年M月D日");
|
||||
const { last_test_result = null } = ntrpData || {};
|
||||
const { ntrp_level, create_time, id } = last_test_result || {};
|
||||
const lastTestTime = create_time ? dayjs(create_time).format("YYYY年M月D日") : "";
|
||||
|
||||
// 组件初始化时立即获取用户信息
|
||||
useEffect(() => {
|
||||
// 如果用户信息为空,立即获取
|
||||
if (!userInfo || Object.keys(userInfo).length === 0) {
|
||||
fetchUserInfo();
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getLastResult();
|
||||
@@ -160,7 +168,10 @@ function Intro() {
|
||||
if (res.code === 0) {
|
||||
setNtrpData(res.data);
|
||||
if (res.data.has_ntrp_level) {
|
||||
fetchUserInfo();
|
||||
// 确保用户信息已加载
|
||||
if (!userInfo || Object.keys(userInfo).length === 0) {
|
||||
await fetchUserInfo();
|
||||
}
|
||||
}
|
||||
setReady(true);
|
||||
}
|
||||
@@ -197,7 +208,7 @@ function Intro() {
|
||||
<View className={styles.avatar}>
|
||||
<Image
|
||||
className={styles.avatarUrl}
|
||||
src={userInfo.avatar_url}
|
||||
src={userInfo?.avatar_url || ""}
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</View>
|
||||
@@ -225,7 +236,7 @@ function Intro() {
|
||||
</View>
|
||||
<View className={styles.levelWrap}>
|
||||
<Text>NTRP</Text>
|
||||
<Text className={styles.level}>{formatNtrpDisplay(ntrp_level)}</Text>
|
||||
<Text className={styles.level}>{formatNtrpDisplay(ntrp_level || "")}</Text>
|
||||
</View>
|
||||
<View className={styles.slogan}>
|
||||
<Text>变线+网前,下一步就是赢比赛!</Text>
|
||||
@@ -439,9 +450,20 @@ function Result() {
|
||||
[propName: string, prop: number][]
|
||||
>([]);
|
||||
|
||||
// 组件初始化时立即获取用户信息
|
||||
useEffect(() => {
|
||||
// 如果用户信息为空,立即获取
|
||||
if (!userInfo || Object.keys(userInfo).length === 0) {
|
||||
fetchUserInfo();
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getResultById();
|
||||
fetchUserInfo();
|
||||
// 确保用户信息已加载
|
||||
if (!userInfo || Object.keys(userInfo).length === 0) {
|
||||
fetchUserInfo();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
async function getResultById() {
|
||||
@@ -548,7 +570,7 @@ function Result() {
|
||||
|
||||
async function handleSaveImage() {
|
||||
console.log(userInfo);
|
||||
if (!userInfo.id) {
|
||||
if (!userInfo?.id) {
|
||||
return;
|
||||
}
|
||||
Taro.getSetting().then(async (res) => {
|
||||
@@ -597,7 +619,7 @@ function Result() {
|
||||
});
|
||||
|
||||
function handleAuth() {
|
||||
if (userInfo.id) {
|
||||
if (userInfo?.id) {
|
||||
return true;
|
||||
}
|
||||
const currentPage = getCurrentFullPath();
|
||||
@@ -616,7 +638,7 @@ function Result() {
|
||||
<View className={styles.avatar}>
|
||||
<Image
|
||||
className={styles.avatarUrl}
|
||||
src={userInfo.avatar_url}
|
||||
src={userInfo?.avatar_url || ""}
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</View>
|
||||
@@ -649,9 +671,9 @@ function Result() {
|
||||
<Text>重新测试</Text>
|
||||
</View>
|
||||
</View>
|
||||
{userInfo.id ? (
|
||||
{userInfo?.id ? (
|
||||
<View className={styles.updateTip}>
|
||||
<Text>你的 NTRP 水平已更新为 {formatNtrpDisplay(result?.ntrp_level)} </Text>
|
||||
<Text>你的 NTRP 水平已更新为 {formatNtrpDisplay(result?.ntrp_level || "")} </Text>
|
||||
<Text className={styles.grayTip}>(可在个人信息中修改)</Text>
|
||||
</View>
|
||||
) : (
|
||||
@@ -669,7 +691,7 @@ function Result() {
|
||||
<View className={styles.share}>
|
||||
<Button
|
||||
className={styles.shareBtn}
|
||||
openType={userInfo.id ? "share" : undefined}
|
||||
openType={userInfo?.id ? "share" : undefined}
|
||||
onClick={handleAuth}
|
||||
></Button>
|
||||
<View className={styles.shareBtnCover}>
|
||||
|
||||
@@ -11,27 +11,48 @@ export interface UserState {
|
||||
updateUserInfo: (userInfo: Partial<UserInfoType>) => void;
|
||||
}
|
||||
|
||||
const fetchUserInfo = async (set) => {
|
||||
try {
|
||||
const res = await fetchUserProfile();
|
||||
set({ user: res.data });
|
||||
return res.data;
|
||||
} catch {}
|
||||
// 请求锁,防止重复请求
|
||||
let fetchingUserInfo = false;
|
||||
let fetchUserInfoPromise: Promise<UserInfoType | undefined> | null = null;
|
||||
|
||||
const fetchUserInfoWithLock = async (set) => {
|
||||
// 如果正在请求,直接返回现有的 Promise
|
||||
if (fetchingUserInfo && fetchUserInfoPromise) {
|
||||
return fetchUserInfoPromise;
|
||||
}
|
||||
|
||||
fetchingUserInfo = true;
|
||||
fetchUserInfoPromise = (async () => {
|
||||
try {
|
||||
const res = await fetchUserProfile();
|
||||
set({ user: res.data });
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
console.error("获取用户信息失败:", error);
|
||||
return undefined;
|
||||
} finally {
|
||||
fetchingUserInfo = false;
|
||||
fetchUserInfoPromise = null;
|
||||
}
|
||||
})();
|
||||
|
||||
return fetchUserInfoPromise;
|
||||
};
|
||||
|
||||
export const useUser = create<UserState>()((set) => ({
|
||||
user: {},
|
||||
fetchUserInfo: fetchUserInfo.bind(null, set),
|
||||
fetchUserInfo: () => fetchUserInfoWithLock(set),
|
||||
updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
|
||||
try {
|
||||
// 先更新后端
|
||||
await updateUserProfile(userInfo);
|
||||
// 然后立即更新本地状态
|
||||
// 然后立即更新本地状态(乐观更新)
|
||||
set((state) => ({
|
||||
user: { ...state.user, ...userInfo },
|
||||
}));
|
||||
// 最后重新获取完整用户信息确保数据一致性
|
||||
await fetchUserInfo(set);
|
||||
// 不再每次都重新获取完整用户信息,减少请求次数
|
||||
// 只有在更新头像等需要服务器返回新URL的字段时才需要重新获取
|
||||
// 如果需要确保数据一致性,可以在特定场景下手动调用 fetchUserInfo
|
||||
} catch (error) {
|
||||
console.error("更新用户信息失败:", error);
|
||||
throw error;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { View, Text, Image, ScrollView, Button } from "@tarojs/components";
|
||||
import { View, Text, Image, Button } from "@tarojs/components";
|
||||
import { PopupPicker } from "@/components/Picker/index";
|
||||
import Taro from "@tarojs/taro";
|
||||
import "./index.scss";
|
||||
@@ -19,18 +19,22 @@ const EditProfilePage: React.FC = () => {
|
||||
const user_info = useUserInfo();
|
||||
|
||||
// 表单状态,基于store中的用户信息初始化
|
||||
const [form_data, setFormData] = useState({
|
||||
nickname: (user_info as UserInfoType)?.nickname || "",
|
||||
personal_profile: (user_info as UserInfoType)?.personal_profile || "",
|
||||
occupation: (user_info as UserInfoType)?.occupation || "",
|
||||
ntrp_level: (user_info as UserInfoType)?.ntrp_level || "4.0",
|
||||
phone: (user_info as UserInfoType)?.phone || "",
|
||||
gender: (user_info as UserInfoType)?.gender || "",
|
||||
birthday: (user_info as UserInfoType)?.birthday || "2000-01-01",
|
||||
country: (user_info as UserInfoType)?.country || "",
|
||||
province: (user_info as UserInfoType)?.province || "",
|
||||
city: (user_info as UserInfoType)?.city || "",
|
||||
});
|
||||
const getInitialFormData = () => {
|
||||
const info = user_info as UserInfoType;
|
||||
return {
|
||||
nickname: info?.nickname ?? "",
|
||||
personal_profile: info?.personal_profile ?? "",
|
||||
occupation: info?.occupation ?? "",
|
||||
ntrp_level: info?.ntrp_level ?? "4.0",
|
||||
phone: info?.phone ?? "",
|
||||
gender: info?.gender ?? "",
|
||||
birthday: info?.birthday ?? "2000-01-01",
|
||||
country: info?.country ?? "",
|
||||
province: info?.province ?? "",
|
||||
city: info?.city ?? "",
|
||||
};
|
||||
};
|
||||
const [form_data, setFormData] = useState(getInitialFormData());
|
||||
|
||||
// 加载状态
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -55,17 +59,18 @@ const EditProfilePage: React.FC = () => {
|
||||
// 监听store中的用户信息变化,同步到表单状态
|
||||
useEffect(() => {
|
||||
if (user_info && Object.keys(user_info).length > 0) {
|
||||
const info = user_info as UserInfoType;
|
||||
setFormData({
|
||||
nickname: (user_info as UserInfoType)?.nickname || "",
|
||||
personal_profile: (user_info as UserInfoType)?.personal_profile || "",
|
||||
occupation: (user_info as UserInfoType)?.occupation || "",
|
||||
ntrp_level: (user_info as UserInfoType)?.ntrp_level || "4.0",
|
||||
phone: (user_info as UserInfoType)?.phone || "",
|
||||
gender: (user_info as UserInfoType)?.gender || "",
|
||||
birthday: (user_info as UserInfoType)?.birthday || "2000-01-01",
|
||||
country: (user_info as UserInfoType)?.country || "",
|
||||
province: (user_info as UserInfoType)?.province || "",
|
||||
city: (user_info as UserInfoType)?.city || "",
|
||||
nickname: info?.nickname ?? "",
|
||||
personal_profile: info?.personal_profile ?? "",
|
||||
occupation: info?.occupation ?? "",
|
||||
ntrp_level: info?.ntrp_level ?? "4.0",
|
||||
phone: info?.phone ?? "",
|
||||
gender: info?.gender ?? "",
|
||||
birthday: info?.birthday ?? "2000-01-01",
|
||||
country: info?.country ?? "",
|
||||
province: info?.province ?? "",
|
||||
city: info?.city ?? "",
|
||||
});
|
||||
}
|
||||
}, [user_info]);
|
||||
@@ -183,6 +188,14 @@ const EditProfilePage: React.FC = () => {
|
||||
|
||||
const handle_edit_modal_save = async (value: string) => {
|
||||
try {
|
||||
// 验证值不能是 undefined 或 null
|
||||
if (value === undefined || value === null) {
|
||||
Taro.showToast({
|
||||
title: "数据不完整,请重新输入",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 调用更新用户信息接口,只传递修改的字段
|
||||
const update_data = { [editing_field]: value };
|
||||
await updateUserInfo(update_data);
|
||||
@@ -224,11 +237,30 @@ const EditProfilePage: React.FC = () => {
|
||||
field !== null &&
|
||||
!Array.isArray(field)
|
||||
) {
|
||||
// 验证对象中的值不能是 undefined
|
||||
const hasUndefined = Object.values(field).some(
|
||||
(v) => v === undefined || v === null
|
||||
);
|
||||
if (hasUndefined) {
|
||||
Taro.showToast({
|
||||
title: "数据不完整,请重新选择",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
await updateUserInfo({ ...field });
|
||||
|
||||
// 更新表单状态(store会自动更新)
|
||||
setFormData((prev) => ({ ...prev, ...field }));
|
||||
} else {
|
||||
// 验证值不能是 undefined
|
||||
if (value === undefined || value === null) {
|
||||
Taro.showToast({
|
||||
title: "数据不完整,请重新选择",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 调用更新用户信息接口,只传递修改的字段
|
||||
const update_data = { [field as string]: value };
|
||||
await updateUserInfo(update_data);
|
||||
@@ -252,12 +284,26 @@ const EditProfilePage: React.FC = () => {
|
||||
|
||||
// 处理性别选择
|
||||
const handle_gender_change = (e: any) => {
|
||||
if (!Array.isArray(e) || e.length === 0 || e[0] === undefined) {
|
||||
Taro.showToast({
|
||||
title: "请选择性别",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const gender_value = e[0];
|
||||
handle_field_edit("gender", gender_value);
|
||||
handle_field_edit("gender", String(gender_value));
|
||||
};
|
||||
|
||||
// 处理生日选择
|
||||
const handle_birthday_change = (e: any) => {
|
||||
if (!Array.isArray(e) || e.length < 3 || e.some((v) => v === undefined)) {
|
||||
Taro.showToast({
|
||||
title: "请完整选择生日",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const [year, month, day] = e;
|
||||
handle_field_edit(
|
||||
"birthday",
|
||||
@@ -270,20 +316,46 @@ const EditProfilePage: React.FC = () => {
|
||||
|
||||
// 处理地区选择
|
||||
const handle_location_change = (e: any) => {
|
||||
if (!Array.isArray(e) || e.length < 3 || e.some((v) => v === undefined || v === null)) {
|
||||
Taro.showToast({
|
||||
title: "请完整选择地区",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const [country, province, city] = e;
|
||||
handle_field_edit({ country, province, city });
|
||||
handle_field_edit({
|
||||
country: String(country ?? ""),
|
||||
province: String(province ?? ""),
|
||||
city: String(city ?? "")
|
||||
});
|
||||
};
|
||||
|
||||
// 处理NTRP水平选择
|
||||
const handle_ntrp_level_change = (e: any) => {
|
||||
if (!Array.isArray(e) || e.length === 0 || e[0] === undefined) {
|
||||
Taro.showToast({
|
||||
title: "请选择NTRP水平",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const ntrp_level_value = e[0];
|
||||
handle_field_edit("ntrp_level", ntrp_level_value);
|
||||
handle_field_edit("ntrp_level", String(ntrp_level_value));
|
||||
};
|
||||
|
||||
// 处理职业选择
|
||||
const handle_occupation_change = (e: any) => {
|
||||
const [country, province, city] = e;
|
||||
handle_field_edit("occupation", `${country} ${province} ${city}`);
|
||||
if (!Array.isArray(e) || e.length === 0 || e.some((v) => v === undefined || v === null)) {
|
||||
Taro.showToast({
|
||||
title: "请完整选择职业",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 职业可能是多级联动,将所有选中的值用空格连接
|
||||
const occupation_value = e.map((v) => String(v ?? "")).filter(Boolean).join(" ");
|
||||
handle_field_edit("occupation", occupation_value);
|
||||
};
|
||||
|
||||
// 处理退出登录
|
||||
@@ -375,7 +447,7 @@ const EditProfilePage: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
{/* 主要内容 */}
|
||||
<ScrollView className="edit_main_content" scrollY>
|
||||
<View className="edit_main_content">
|
||||
{loading ? (
|
||||
<View className="loading_container">
|
||||
<Text className="loading_text">加载中...</Text>
|
||||
@@ -635,7 +707,7 @@ const EditProfilePage: React.FC = () => {
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* 编辑弹窗 */}
|
||||
<EditModal
|
||||
|
||||
Reference in New Issue
Block a user