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

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) => {
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>
</>

View File

@@ -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}>

View File

@@ -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;

View File

@@ -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