修复分享页面不能访问问题

This commit is contained in:
张成
2025-11-17 23:39:55 +08:00
parent ed9c0e9768
commit 4568e758a7
4 changed files with 199 additions and 59 deletions

View File

@@ -51,7 +51,12 @@ const PopupPicker = ({
}: PickerProps) => { }: PickerProps) => {
const [defaultValue, setDefaultValue] = useState<(string | number)[]>([]); const [defaultValue, setDefaultValue] = useState<(string | number)[]>([]);
const [defaultOptions, setDefaultOptions] = useState<PickerOption[][]>([]); const [defaultOptions, setDefaultOptions] = useState<PickerOption[][]>([]);
const [pickerCurrentValue, setPickerCurrentValue] = useState<(string | number)[]>(value);
const changePicker = (options: any[], values: any, columnIndex: number) => { const changePicker = (options: any[], values: any, columnIndex: number) => {
// 更新 Picker 的当前值
setPickerCurrentValue(values);
if (onChange) { if (onChange) {
console.log("picker onChange", columnIndex, values, options); console.log("picker onChange", columnIndex, values, options);
if ( if (
@@ -77,8 +82,19 @@ const PopupPicker = ({
} }
}; };
// 处理 Picker 的确认事件,获取当前选中的值
const handlePickerConfirm = (options: PickerOption[], values: (string | number)[]) => {
setPickerCurrentValue(values);
setDefaultValue(values);
};
const handleConfirm = () => { const handleConfirm = () => {
onChange(defaultValue); // 如果用户滚动过defaultValue 不为空),使用 defaultValue
// 如果用户没有滚动defaultValue 为空),使用传入的默认值(即当前显示的默认选项)
const valueToSave = defaultValue.length > 0 ? defaultValue : value;
if (onChange && valueToSave.length > 0) {
onChange(valueToSave);
}
setvisible(false); setvisible(false);
}; };
@@ -97,11 +113,19 @@ const PopupPicker = ({
} }
}, [type]); }, [type]);
// useEffect(() => { // 当选择器打开时,初始化 defaultValue 和 pickerCurrentValue
// if (value.length > 0 && defaultOptions.length > 0) { useEffect(() => {
// setDefaultValue([...value]) if (visible) {
// } if (value.length > 0) {
// }, [value, defaultOptions]) setDefaultValue([...value]);
setPickerCurrentValue([...value]);
} else {
// 如果 value 为空,重置为初始状态
setDefaultValue([]);
setPickerCurrentValue([]);
}
}
}, [visible, value]);
return ( return (
<> <>
<CommonPopup <CommonPopup
@@ -147,6 +171,7 @@ const PopupPicker = ({
options={defaultOptions} options={defaultOptions}
defaultValue={value} defaultValue={value}
onChange={changePicker} onChange={changePicker}
onConfirm={handlePickerConfirm}
/> />
</CommonPopup> </CommonPopup>
</> </>

View File

@@ -147,9 +147,17 @@ function Intro() {
const [ready, setReady] = useState(false); const [ready, setReady] = useState(false);
const { setCallback } = useEvaluate(); const { setCallback } = useEvaluate();
const { last_test_result: { ntrp_level, create_time, id } = {} } = const { last_test_result = null } = ntrpData || {};
ntrpData || {}; const { ntrp_level, create_time, id } = last_test_result || {};
const lastTestTime = dayjs(create_time).format("YYYY年M月D日"); const lastTestTime = create_time ? dayjs(create_time).format("YYYY年M月D日") : "";
// 组件初始化时立即获取用户信息
useEffect(() => {
// 如果用户信息为空,立即获取
if (!userInfo || Object.keys(userInfo).length === 0) {
fetchUserInfo();
}
}, []);
useEffect(() => { useEffect(() => {
getLastResult(); getLastResult();
@@ -160,7 +168,10 @@ function Intro() {
if (res.code === 0) { if (res.code === 0) {
setNtrpData(res.data); setNtrpData(res.data);
if (res.data.has_ntrp_level) { if (res.data.has_ntrp_level) {
fetchUserInfo(); // 确保用户信息已加载
if (!userInfo || Object.keys(userInfo).length === 0) {
await fetchUserInfo();
}
} }
setReady(true); setReady(true);
} }
@@ -197,7 +208,7 @@ function Intro() {
<View className={styles.avatar}> <View className={styles.avatar}>
<Image <Image
className={styles.avatarUrl} className={styles.avatarUrl}
src={userInfo.avatar_url} src={userInfo?.avatar_url || ""}
mode="aspectFill" mode="aspectFill"
/> />
</View> </View>
@@ -225,7 +236,7 @@ function Intro() {
</View> </View>
<View className={styles.levelWrap}> <View className={styles.levelWrap}>
<Text>NTRP</Text> <Text>NTRP</Text>
<Text className={styles.level}>{formatNtrpDisplay(ntrp_level)}</Text> <Text className={styles.level}>{formatNtrpDisplay(ntrp_level || "")}</Text>
</View> </View>
<View className={styles.slogan}> <View className={styles.slogan}>
<Text>线+</Text> <Text>线+</Text>
@@ -439,9 +450,20 @@ function Result() {
[propName: string, prop: number][] [propName: string, prop: number][]
>([]); >([]);
// 组件初始化时立即获取用户信息
useEffect(() => {
// 如果用户信息为空,立即获取
if (!userInfo || Object.keys(userInfo).length === 0) {
fetchUserInfo();
}
}, []);
useEffect(() => { useEffect(() => {
getResultById(); getResultById();
fetchUserInfo(); // 确保用户信息已加载
if (!userInfo || Object.keys(userInfo).length === 0) {
fetchUserInfo();
}
}, [id]); }, [id]);
async function getResultById() { async function getResultById() {
@@ -548,7 +570,7 @@ function Result() {
async function handleSaveImage() { async function handleSaveImage() {
console.log(userInfo); console.log(userInfo);
if (!userInfo.id) { if (!userInfo?.id) {
return; return;
} }
Taro.getSetting().then(async (res) => { Taro.getSetting().then(async (res) => {
@@ -597,7 +619,7 @@ function Result() {
}); });
function handleAuth() { function handleAuth() {
if (userInfo.id) { if (userInfo?.id) {
return true; return true;
} }
const currentPage = getCurrentFullPath(); const currentPage = getCurrentFullPath();
@@ -616,7 +638,7 @@ function Result() {
<View className={styles.avatar}> <View className={styles.avatar}>
<Image <Image
className={styles.avatarUrl} className={styles.avatarUrl}
src={userInfo.avatar_url} src={userInfo?.avatar_url || ""}
mode="aspectFill" mode="aspectFill"
/> />
</View> </View>
@@ -649,9 +671,9 @@ function Result() {
<Text></Text> <Text></Text>
</View> </View>
</View> </View>
{userInfo.id ? ( {userInfo?.id ? (
<View className={styles.updateTip}> <View className={styles.updateTip}>
<Text> NTRP {formatNtrpDisplay(result?.ntrp_level)} </Text> <Text> NTRP {formatNtrpDisplay(result?.ntrp_level || "")} </Text>
<Text className={styles.grayTip}>()</Text> <Text className={styles.grayTip}>()</Text>
</View> </View>
) : ( ) : (
@@ -669,7 +691,7 @@ function Result() {
<View className={styles.share}> <View className={styles.share}>
<Button <Button
className={styles.shareBtn} className={styles.shareBtn}
openType={userInfo.id ? "share" : undefined} openType={userInfo?.id ? "share" : undefined}
onClick={handleAuth} onClick={handleAuth}
></Button> ></Button>
<View className={styles.shareBtnCover}> <View className={styles.shareBtnCover}>

View File

@@ -11,27 +11,48 @@ export interface UserState {
updateUserInfo: (userInfo: Partial<UserInfoType>) => void; updateUserInfo: (userInfo: Partial<UserInfoType>) => void;
} }
const fetchUserInfo = async (set) => { // 请求锁,防止重复请求
try { let fetchingUserInfo = false;
const res = await fetchUserProfile(); let fetchUserInfoPromise: Promise<UserInfoType | undefined> | null = null;
set({ user: res.data });
return res.data; const fetchUserInfoWithLock = async (set) => {
} catch {} // 如果正在请求,直接返回现有的 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) => ({ export const useUser = create<UserState>()((set) => ({
user: {}, user: {},
fetchUserInfo: fetchUserInfo.bind(null, set), fetchUserInfo: () => fetchUserInfoWithLock(set),
updateUserInfo: async (userInfo: Partial<UserInfoType>) => { updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
try { try {
// 先更新后端 // 先更新后端
await updateUserProfile(userInfo); await updateUserProfile(userInfo);
// 然后立即更新本地状态 // 然后立即更新本地状态(乐观更新)
set((state) => ({ set((state) => ({
user: { ...state.user, ...userInfo }, user: { ...state.user, ...userInfo },
})); }));
// 最后重新获取完整用户信息确保数据一致性 // 不再每次都重新获取完整用户信息,减少请求次数
await fetchUserInfo(set); // 只有在更新头像等需要服务器返回新URL的字段时才需要重新获取
// 如果需要确保数据一致性,可以在特定场景下手动调用 fetchUserInfo
} catch (error) { } catch (error) {
console.error("更新用户信息失败:", error); console.error("更新用户信息失败:", error);
throw error; throw error;

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; 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 { PopupPicker } from "@/components/Picker/index";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import "./index.scss"; import "./index.scss";
@@ -19,18 +19,22 @@ const EditProfilePage: React.FC = () => {
const user_info = useUserInfo(); const user_info = useUserInfo();
// 表单状态基于store中的用户信息初始化 // 表单状态基于store中的用户信息初始化
const [form_data, setFormData] = useState({ const getInitialFormData = () => {
nickname: (user_info as UserInfoType)?.nickname || "", const info = user_info as UserInfoType;
personal_profile: (user_info as UserInfoType)?.personal_profile || "", return {
occupation: (user_info as UserInfoType)?.occupation || "", nickname: info?.nickname ?? "",
ntrp_level: (user_info as UserInfoType)?.ntrp_level || "4.0", personal_profile: info?.personal_profile ?? "",
phone: (user_info as UserInfoType)?.phone || "", occupation: info?.occupation ?? "",
gender: (user_info as UserInfoType)?.gender || "", ntrp_level: info?.ntrp_level ?? "4.0",
birthday: (user_info as UserInfoType)?.birthday || "2000-01-01", phone: info?.phone ?? "",
country: (user_info as UserInfoType)?.country || "", gender: info?.gender ?? "",
province: (user_info as UserInfoType)?.province || "", birthday: info?.birthday ?? "2000-01-01",
city: (user_info as UserInfoType)?.city || "", country: info?.country ?? "",
}); province: info?.province ?? "",
city: info?.city ?? "",
};
};
const [form_data, setFormData] = useState(getInitialFormData());
// 加载状态 // 加载状态
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -55,17 +59,18 @@ const EditProfilePage: React.FC = () => {
// 监听store中的用户信息变化同步到表单状态 // 监听store中的用户信息变化同步到表单状态
useEffect(() => { useEffect(() => {
if (user_info && Object.keys(user_info).length > 0) { if (user_info && Object.keys(user_info).length > 0) {
const info = user_info as UserInfoType;
setFormData({ setFormData({
nickname: (user_info as UserInfoType)?.nickname || "", nickname: info?.nickname ?? "",
personal_profile: (user_info as UserInfoType)?.personal_profile || "", personal_profile: info?.personal_profile ?? "",
occupation: (user_info as UserInfoType)?.occupation || "", occupation: info?.occupation ?? "",
ntrp_level: (user_info as UserInfoType)?.ntrp_level || "4.0", ntrp_level: info?.ntrp_level ?? "4.0",
phone: (user_info as UserInfoType)?.phone || "", phone: info?.phone ?? "",
gender: (user_info as UserInfoType)?.gender || "", gender: info?.gender ?? "",
birthday: (user_info as UserInfoType)?.birthday || "2000-01-01", birthday: info?.birthday ?? "2000-01-01",
country: (user_info as UserInfoType)?.country || "", country: info?.country ?? "",
province: (user_info as UserInfoType)?.province || "", province: info?.province ?? "",
city: (user_info as UserInfoType)?.city || "", city: info?.city ?? "",
}); });
} }
}, [user_info]); }, [user_info]);
@@ -183,6 +188,14 @@ const EditProfilePage: React.FC = () => {
const handle_edit_modal_save = async (value: string) => { const handle_edit_modal_save = async (value: string) => {
try { try {
// 验证值不能是 undefined 或 null
if (value === undefined || value === null) {
Taro.showToast({
title: "数据不完整,请重新输入",
icon: "none",
});
return;
}
// 调用更新用户信息接口,只传递修改的字段 // 调用更新用户信息接口,只传递修改的字段
const update_data = { [editing_field]: value }; const update_data = { [editing_field]: value };
await updateUserInfo(update_data); await updateUserInfo(update_data);
@@ -224,11 +237,30 @@ const EditProfilePage: React.FC = () => {
field !== null && field !== null &&
!Array.isArray(field) !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 }); await updateUserInfo({ ...field });
// 更新表单状态store会自动更新 // 更新表单状态store会自动更新
setFormData((prev) => ({ ...prev, ...field })); setFormData((prev) => ({ ...prev, ...field }));
} else { } else {
// 验证值不能是 undefined
if (value === undefined || value === null) {
Taro.showToast({
title: "数据不完整,请重新选择",
icon: "none",
});
return;
}
// 调用更新用户信息接口,只传递修改的字段 // 调用更新用户信息接口,只传递修改的字段
const update_data = { [field as string]: value }; const update_data = { [field as string]: value };
await updateUserInfo(update_data); await updateUserInfo(update_data);
@@ -252,12 +284,26 @@ const EditProfilePage: React.FC = () => {
// 处理性别选择 // 处理性别选择
const handle_gender_change = (e: any) => { 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]; const gender_value = e[0];
handle_field_edit("gender", gender_value); handle_field_edit("gender", String(gender_value));
}; };
// 处理生日选择 // 处理生日选择
const handle_birthday_change = (e: any) => { 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; const [year, month, day] = e;
handle_field_edit( handle_field_edit(
"birthday", "birthday",
@@ -270,20 +316,46 @@ const EditProfilePage: React.FC = () => {
// 处理地区选择 // 处理地区选择
const handle_location_change = (e: any) => { 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; const [country, province, city] = e;
handle_field_edit({ country, province, city }); handle_field_edit({
country: String(country ?? ""),
province: String(province ?? ""),
city: String(city ?? "")
});
}; };
// 处理NTRP水平选择 // 处理NTRP水平选择
const handle_ntrp_level_change = (e: any) => { 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]; 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 handle_occupation_change = (e: any) => {
const [country, province, city] = e; if (!Array.isArray(e) || e.length === 0 || e.some((v) => v === undefined || v === null)) {
handle_field_edit("occupation", `${country} ${province} ${city}`); 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 ? ( {loading ? (
<View className="loading_container"> <View className="loading_container">
<Text className="loading_text">...</Text> <Text className="loading_text">...</Text>
@@ -635,7 +707,7 @@ const EditProfilePage: React.FC = () => {
</View> </View>
</> </>
)} )}
</ScrollView> </View>
{/* 编辑弹窗 */} {/* 编辑弹窗 */}
<EditModal <EditModal