优化个人页
This commit is contained in:
@@ -17,6 +17,8 @@ interface PickerOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PickerProps {
|
interface PickerProps {
|
||||||
|
minYear?: number;
|
||||||
|
maxYear?: number;
|
||||||
title?: string;
|
title?: string;
|
||||||
showHeader?: boolean;
|
showHeader?: boolean;
|
||||||
confirmText?: string;
|
confirmText?: string;
|
||||||
@@ -28,10 +30,12 @@ interface PickerProps {
|
|||||||
img?: string;
|
img?: string;
|
||||||
onConfirm?: (options: PickerOption[], values: (string | number)[]) => void;
|
onConfirm?: (options: PickerOption[], values: (string | number)[]) => void;
|
||||||
onChange?: (value: (string | number)[]) => void;
|
onChange?: (value: (string | number)[]) => void;
|
||||||
style?: React.CSSProperties
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PopupPicker = ({
|
const PopupPicker = ({
|
||||||
|
minYear,
|
||||||
|
maxYear,
|
||||||
confirmText,
|
confirmText,
|
||||||
title,
|
title,
|
||||||
showHeader,
|
showHeader,
|
||||||
@@ -87,7 +91,7 @@ const PopupPicker = ({
|
|||||||
if (type === "month") {
|
if (type === "month") {
|
||||||
setDefaultOptions(renderYearMonth());
|
setDefaultOptions(renderYearMonth());
|
||||||
} else if (type === "day") {
|
} else if (type === "day") {
|
||||||
setDefaultOptions(renderYearMonthDay());
|
setDefaultOptions(renderYearMonthDay(minYear, maxYear));
|
||||||
} else if (type === "hour") {
|
} else if (type === "hour") {
|
||||||
setDefaultOptions(renderHourMinute());
|
setDefaultOptions(renderHourMinute());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,45 +6,47 @@ import "./index.scss";
|
|||||||
import { EditModal } from "@/components";
|
import { EditModal } from "@/components";
|
||||||
import { UserService, PickerOption } from "@/services/userService";
|
import { UserService, PickerOption } from "@/services/userService";
|
||||||
import { PopupPicker } from "@/components/Picker/index";
|
import { PopupPicker } from "@/components/Picker/index";
|
||||||
|
import { useUserActions } from "@/store/userStore";
|
||||||
|
import { UserInfoType } from "@/services/userService";
|
||||||
|
|
||||||
// 用户信息接口
|
// 用户信息接口
|
||||||
export interface UserInfo {
|
// export interface UserInfo {
|
||||||
id: string | number;
|
// id: string | number;
|
||||||
nickname: string;
|
// nickname: string;
|
||||||
avatar: string;
|
// avatar: string;
|
||||||
join_date: string;
|
// join_date: string;
|
||||||
stats: {
|
// stats: {
|
||||||
following: number;
|
// following: number;
|
||||||
friends: number;
|
// friends: number;
|
||||||
hosted: number;
|
// hosted: number;
|
||||||
participated: number;
|
// participated: number;
|
||||||
};
|
// };
|
||||||
personal_profile: string;
|
// personal_profile: string;
|
||||||
occupation: string;
|
// occupation: string;
|
||||||
ntrp_level: string;
|
// ntrp_level: string;
|
||||||
phone?: string;
|
// phone?: string;
|
||||||
gender: string;
|
// gender: string;
|
||||||
bio?: string;
|
// bio?: string;
|
||||||
latitude?: string;
|
// latitude?: string;
|
||||||
longitude?: string;
|
// longitude?: string;
|
||||||
birthday?: string;
|
// birthday?: string;
|
||||||
is_following?: boolean;
|
// is_following?: boolean;
|
||||||
tags?: string[];
|
// tags?: string[];
|
||||||
ongoing_games?: string[];
|
// ongoing_games?: string[];
|
||||||
country: string;
|
// country: string;
|
||||||
province: string;
|
// province: string;
|
||||||
city: string;
|
// city: string;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 用户信息卡片组件属性
|
// 用户信息卡片组件属性
|
||||||
interface UserInfoCardProps {
|
interface UserInfoCardProps {
|
||||||
user_info: UserInfo;
|
user_info: Partial<UserInfoType>;
|
||||||
is_current_user: boolean;
|
is_current_user: boolean;
|
||||||
is_following?: boolean;
|
is_following?: boolean;
|
||||||
on_follow?: () => void;
|
on_follow?: () => void;
|
||||||
on_message?: () => void;
|
on_message?: () => void;
|
||||||
on_share?: () => void;
|
on_share?: () => void;
|
||||||
set_user_info?: (info: UserInfo) => void;
|
set_user_info?: (info: Partial<UserInfoType>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理编辑用户信息
|
// 处理编辑用户信息
|
||||||
@@ -63,6 +65,7 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
on_share,
|
on_share,
|
||||||
set_user_info,
|
set_user_info,
|
||||||
}) => {
|
}) => {
|
||||||
|
const { updateUserInfo } = useUserActions();
|
||||||
console.log("UserInfoCard 用户信息:", user_info);
|
console.log("UserInfoCard 用户信息:", user_info);
|
||||||
// 编辑个人简介弹窗状态
|
// 编辑个人简介弹窗状态
|
||||||
const [edit_modal_visible, setEditModalVisible] = useState(false);
|
const [edit_modal_visible, setEditModalVisible] = useState(false);
|
||||||
@@ -74,7 +77,9 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
// 表单状态
|
// 表单状态
|
||||||
const [form_data, setFormData] = useState<UserInfo>({ ...user_info });
|
const [form_data, setFormData] = useState<Partial<UserInfoType>>({
|
||||||
|
...user_info,
|
||||||
|
});
|
||||||
|
|
||||||
// 职业数据
|
// 职业数据
|
||||||
const [professions, setProfessions] = useState<PickerOption[]>([]);
|
const [professions, setProfessions] = useState<PickerOption[]>([]);
|
||||||
@@ -172,15 +177,14 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
field !== null &&
|
field !== null &&
|
||||||
!Array.isArray(field)
|
!Array.isArray(field)
|
||||||
) {
|
) {
|
||||||
await UserService.update_user_info({ ...field });
|
await updateUserInfo({ ...field });
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
setFormData((prev) => ({ ...prev, ...field }));
|
setFormData((prev) => ({ ...prev, ...field }));
|
||||||
// setUserInfo((prev) => ({ ...prev, ...field }));
|
// setUserInfo((prev) => ({ ...prev, ...field }));
|
||||||
} else {
|
} else {
|
||||||
// 调用更新用户信息接口,只传递修改的字段
|
// 调用更新用户信息接口,只传递修改的字段
|
||||||
const update_data = { [field as string]: value };
|
const update_data = { [field as string]: value };
|
||||||
await UserService.update_user_info(update_data);
|
await updateUserInfo(update_data);
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
setFormData((prev) => ({ ...prev, [field as string]: value }));
|
setFormData((prev) => ({ ...prev, [field as string]: value }));
|
||||||
// setUserInfo((prev) => ({ ...prev, [field as string]: value }));
|
// setUserInfo((prev) => ({ ...prev, [field as string]: value }));
|
||||||
@@ -259,11 +263,15 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
{/* 头像和基本信息 */}
|
{/* 头像和基本信息 */}
|
||||||
<View className="basic_info">
|
<View className="basic_info">
|
||||||
<View className="avatar_container">
|
<View className="avatar_container">
|
||||||
<Image className="avatar" src={user_info.avatar} mode="aspectFill" />
|
<Image
|
||||||
|
className="avatar"
|
||||||
|
src={user_info.avatar_url || ""}
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View className="info_container">
|
<View className="info_container">
|
||||||
<Text className="nickname">{user_info.nickname}</Text>
|
<Text className="nickname">{user_info.nickname || ""}</Text>
|
||||||
<Text className="join_date">{user_info.join_date}</Text>
|
<Text className="join_date">{user_info.join_date || ""}</Text>
|
||||||
</View>
|
</View>
|
||||||
{is_current_user && (
|
{is_current_user && (
|
||||||
<View className="tag_item" onClick={on_edit}>
|
<View className="tag_item" onClick={on_edit}>
|
||||||
@@ -282,22 +290,30 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
className="stat_item clickable"
|
className="stat_item clickable"
|
||||||
onClick={() => handle_stats_click("following")}
|
onClick={() => handle_stats_click("following")}
|
||||||
>
|
>
|
||||||
<Text className="stat_number">{user_info.stats.following}</Text>
|
<Text className="stat_number">
|
||||||
|
{user_info.stats?.following_count || 0}
|
||||||
|
</Text>
|
||||||
<Text className="stat_label">关注</Text>
|
<Text className="stat_label">关注</Text>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
className="stat_item clickable"
|
className="stat_item clickable"
|
||||||
onClick={() => handle_stats_click("friends")}
|
onClick={() => handle_stats_click("friends")}
|
||||||
>
|
>
|
||||||
<Text className="stat_number">{user_info.stats.friends}</Text>
|
<Text className="stat_number">
|
||||||
|
{user_info.stats?.followers_count || 0}
|
||||||
|
</Text>
|
||||||
<Text className="stat_label">球友</Text>
|
<Text className="stat_label">球友</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="stat_item">
|
<View className="stat_item">
|
||||||
<Text className="stat_number">{user_info.stats.hosted}</Text>
|
<Text className="stat_number">
|
||||||
|
{user_info.stats?.hosted_games_count || 0}
|
||||||
|
</Text>
|
||||||
<Text className="stat_label">主办</Text>
|
<Text className="stat_label">主办</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="stat_item">
|
<View className="stat_item">
|
||||||
<Text className="stat_number">{user_info.stats.participated}</Text>
|
<Text className="stat_number">
|
||||||
|
{user_info.stats?.participated_games_count || 0}
|
||||||
|
</Text>
|
||||||
<Text className="stat_label">参加</Text>
|
<Text className="stat_label">参加</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -455,7 +471,7 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
]}
|
]}
|
||||||
visible={gender_picker_visible}
|
visible={gender_picker_visible}
|
||||||
setvisible={setGenderPickerVisible}
|
setvisible={setGenderPickerVisible}
|
||||||
value={[form_data.gender]}
|
value={[form_data.gender || ""]}
|
||||||
onChange={handle_gender_change}
|
onChange={handle_gender_change}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -467,7 +483,11 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
options={cities}
|
options={cities}
|
||||||
visible={location_picker_visible}
|
visible={location_picker_visible}
|
||||||
setvisible={setLocationPickerVisible}
|
setvisible={setLocationPickerVisible}
|
||||||
value={[form_data.country, form_data.province, form_data.city]}
|
value={[
|
||||||
|
form_data.country || "",
|
||||||
|
form_data.province || "",
|
||||||
|
form_data.city || "",
|
||||||
|
]}
|
||||||
onChange={handle_location_change}
|
onChange={handle_location_change}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -488,10 +508,10 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
],
|
],
|
||||||
]}
|
]}
|
||||||
type="ntrp"
|
type="ntrp"
|
||||||
img={user_info.avatar}
|
img={user_info.avatar_url || ""}
|
||||||
visible={ntrp_picker_visible}
|
visible={ntrp_picker_visible}
|
||||||
setvisible={setNtrpPickerVisible}
|
setvisible={setNtrpPickerVisible}
|
||||||
value={[form_data.ntrp_level]}
|
value={[form_data.ntrp_level || ""]}
|
||||||
onChange={handle_ntrp_level_change}
|
onChange={handle_ntrp_level_change}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -503,7 +523,7 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
|
|||||||
options={professions}
|
options={professions}
|
||||||
visible={occupation_picker_visible}
|
visible={occupation_picker_visible}
|
||||||
setvisible={setOccupationPickerVisible}
|
setvisible={setOccupationPickerVisible}
|
||||||
value={[...form_data.occupation.split(" ")]}
|
value={[...(form_data.occupation || "").split(" ")]}
|
||||||
onChange={handle_occupation_change}
|
onChange={handle_occupation_change}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -644,7 +664,8 @@ export const GameTabs: React.FC<GameTabsProps> = ({
|
|||||||
<Text className="tab_text">{hosted_text}</Text>
|
<Text className="tab_text">{hosted_text}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
className={`tab_item ${active_tab === "participated" ? "active" : ""
|
className={`tab_item ${
|
||||||
|
active_tab === "participated" ? "active" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => on_tab_change("participated")}
|
onClick={() => on_tab_change("participated")}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { UserInfo } from '@/components/UserInfo';
|
import { UserInfo } from "@/components/UserInfo";
|
||||||
import { API_CONFIG } from '@/config/api';
|
import { API_CONFIG } from "@/config/api";
|
||||||
import httpService, { ApiResponse } from './httpService';
|
import httpService, { ApiResponse } from "./httpService";
|
||||||
import uploadFiles from './uploadFiles';
|
import uploadFiles from "./uploadFiles";
|
||||||
import Taro from '@tarojs/taro';
|
import Taro from "@tarojs/taro";
|
||||||
import getCurrentConfig from '@/config/env';
|
import getCurrentConfig from "@/config/env";
|
||||||
import { clear_login_state } from "@/services/loginService";
|
import { clear_login_state } from "@/services/loginService";
|
||||||
|
|
||||||
|
|
||||||
// 用户详情接口
|
// 用户详情接口
|
||||||
interface UserDetailData {
|
interface UserDetailData {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -33,7 +32,7 @@ interface UserDetailData {
|
|||||||
personal_profile: string;
|
personal_profile: string;
|
||||||
occupation: string;
|
occupation: string;
|
||||||
birthday: string;
|
birthday: string;
|
||||||
ntrp_level: string,
|
ntrp_level: string;
|
||||||
stats: {
|
stats: {
|
||||||
followers_count: number;
|
followers_count: number;
|
||||||
following_count: number;
|
following_count: number;
|
||||||
@@ -55,27 +54,42 @@ export interface Profession {
|
|||||||
|
|
||||||
// 用户详细信息接口(从 loginService 移过来)
|
// 用户详细信息接口(从 loginService 移过来)
|
||||||
export interface UserInfoType {
|
export interface UserInfoType {
|
||||||
id: number
|
id: number;
|
||||||
openid: string
|
openid: string;
|
||||||
unionid: string
|
unionid: string;
|
||||||
session_key: string
|
session_key: string;
|
||||||
nickname: string
|
nickname: string;
|
||||||
avatar_url: string
|
avatar_url: string;
|
||||||
gender: string
|
gender: string;
|
||||||
country: string
|
country: string;
|
||||||
province: string
|
province: string;
|
||||||
city: string
|
city: string;
|
||||||
district: string
|
district: string;
|
||||||
language: string
|
language: string;
|
||||||
phone: string
|
phone: string;
|
||||||
is_subscribed: string
|
is_subscribed: string;
|
||||||
latitude: number
|
latitude: number;
|
||||||
longitude: number
|
longitude: number;
|
||||||
subscribe_time: string
|
subscribe_time: string;
|
||||||
last_login_time: string
|
last_login_time: string;
|
||||||
|
avatar: string;
|
||||||
|
join_date: string;
|
||||||
|
stats: {
|
||||||
|
following_count: number;
|
||||||
|
followers_count: number;
|
||||||
|
hosted_games_count: number;
|
||||||
|
participated_games_count: number;
|
||||||
|
};
|
||||||
|
personal_profile: string;
|
||||||
|
occupation: string;
|
||||||
|
ntrp_level: string;
|
||||||
|
bio?: string;
|
||||||
|
birthday?: string;
|
||||||
|
is_following?: boolean;
|
||||||
|
tags?: string[];
|
||||||
|
ongoing_games?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 后端球局数据接口
|
// 后端球局数据接口
|
||||||
interface BackendGameData {
|
interface BackendGameData {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -138,37 +152,43 @@ const formatOptions = (data: Profession[]): PickerOption[] => {
|
|||||||
const itm: PickerOption = {
|
const itm: PickerOption = {
|
||||||
text,
|
text,
|
||||||
value: text,
|
value: text,
|
||||||
children: children ? formatOptions(children) : []
|
children: children ? formatOptions(children) : [],
|
||||||
}
|
};
|
||||||
if (!itm.children!.length) {
|
if (!itm.children!.length) {
|
||||||
delete itm.children
|
delete itm.children;
|
||||||
}
|
|
||||||
return itm
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
return itm;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 用户服务类
|
// 用户服务类
|
||||||
export class UserService {
|
export class UserService {
|
||||||
// 数据转换函数:将后端数据转换为ListContainer期望的格式
|
// 数据转换函数:将后端数据转换为ListContainer期望的格式
|
||||||
private static transform_game_data(backend_data: BackendGameData[]): any[] {
|
private static transform_game_data(backend_data: BackendGameData[]): any[] {
|
||||||
return backend_data.map(game => {
|
return backend_data.map((game) => {
|
||||||
// 处理时间格式
|
// 处理时间格式
|
||||||
const start_time = new Date(game.start_time.replace(/\s/, 'T'));
|
const start_time = new Date(game.start_time.replace(/\s/, "T"));
|
||||||
const date_time = this.format_date_time(start_time);
|
const date_time = this.format_date_time(start_time);
|
||||||
|
|
||||||
// 处理图片数组 - 兼容两种数据格式
|
// 处理图片数组 - 兼容两种数据格式
|
||||||
let images: string[] = [];
|
let images: string[] = [];
|
||||||
if (game.image_list && game.image_list.length > 0) {
|
if (game.image_list && game.image_list.length > 0) {
|
||||||
images = game.image_list.filter(img => img && img.trim() !== '');
|
images = game.image_list.filter((img) => img && img.trim() !== "");
|
||||||
} else if (game.venue_image_list && game.venue_image_list.length > 0) {
|
} else if (game.venue_image_list && game.venue_image_list.length > 0) {
|
||||||
images = game.venue_image_list
|
images = game.venue_image_list
|
||||||
.filter(img => img && img.url && img.url.trim() !== '')
|
.filter((img) => img && img.url && img.url.trim() !== "")
|
||||||
.map(img => img.url);
|
.map((img) => img.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理距离 - 优先使用venue_dtl中的坐标,其次使用game中的坐标
|
// 处理距离 - 优先使用venue_dtl中的坐标,其次使用game中的坐标
|
||||||
let latitude: number = typeof game.latitude === 'number' ? game.latitude : parseFloat(game.latitude || '0') || 0;
|
let latitude: number =
|
||||||
let longitude: number = typeof game.longitude === 'number' ? game.longitude : parseFloat(game.longitude || '0') || 0;
|
typeof game.latitude === "number"
|
||||||
|
? game.latitude
|
||||||
|
: parseFloat(game.latitude || "0") || 0;
|
||||||
|
let longitude: number =
|
||||||
|
typeof game.longitude === "number"
|
||||||
|
? game.longitude
|
||||||
|
: parseFloat(game.longitude || "0") || 0;
|
||||||
if (game.venue_dtl) {
|
if (game.venue_dtl) {
|
||||||
latitude = parseFloat(game.venue_dtl.latitude) || latitude;
|
latitude = parseFloat(game.venue_dtl.latitude) || latitude;
|
||||||
longitude = parseFloat(game.venue_dtl.longitude) || longitude;
|
longitude = parseFloat(game.venue_dtl.longitude) || longitude;
|
||||||
@@ -176,33 +196,34 @@ export class UserService {
|
|||||||
const distance = this.calculate_distance(latitude, longitude);
|
const distance = this.calculate_distance(latitude, longitude);
|
||||||
|
|
||||||
// 处理地点信息 - 优先使用venue_dtl中的信息
|
// 处理地点信息 - 优先使用venue_dtl中的信息
|
||||||
let location = game.location_name || game.location || '未知地点';
|
let location = game.location_name || game.location || "未知地点";
|
||||||
if (game.venue_dtl && game.venue_dtl.name) {
|
if (game.venue_dtl && game.venue_dtl.name) {
|
||||||
location = game.venue_dtl.name;
|
location = game.venue_dtl.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理人数统计 - 兼容不同的字段名
|
// 处理人数统计 - 兼容不同的字段名
|
||||||
const registered_count = game.current_players || game.participant_count || 0;
|
const registered_count =
|
||||||
|
game.current_players || game.participant_count || 0;
|
||||||
const max_count = game.max_players || game.max_participants || 0;
|
const max_count = game.max_players || game.max_participants || 0;
|
||||||
|
|
||||||
// 转换为 ListCard 期望的格式
|
// 转换为 ListCard 期望的格式
|
||||||
return {
|
return {
|
||||||
id: game.id,
|
id: game.id,
|
||||||
title: game.title || '未命名球局',
|
title: game.title || "未命名球局",
|
||||||
start_time: date_time,
|
start_time: date_time,
|
||||||
original_start_time: game.start_time,
|
original_start_time: game.start_time,
|
||||||
end_time: game.end_time || '',
|
end_time: game.end_time || "",
|
||||||
location: location,
|
location: location,
|
||||||
distance_km: parseFloat(distance.replace('km', '')) || 0,
|
distance_km: parseFloat(distance.replace("km", "")) || 0,
|
||||||
current_players: registered_count,
|
current_players: registered_count,
|
||||||
max_players: max_count,
|
max_players: max_count,
|
||||||
skill_level_min: parseInt(game.skill_level_min) || 0,
|
skill_level_min: parseInt(game.skill_level_min) || 0,
|
||||||
skill_level_max: parseInt(game.skill_level_max) || 0,
|
skill_level_max: parseInt(game.skill_level_max) || 0,
|
||||||
play_type: game.play_type || '不限',
|
play_type: game.play_type || "不限",
|
||||||
image_list: images,
|
image_list: images,
|
||||||
court_type: game.court_type || '未知',
|
court_type: game.court_type || "未知",
|
||||||
matchType: game.play_type || '不限',
|
matchType: game.play_type || "不限",
|
||||||
shinei: game.court_type || '未知',
|
shinei: game.court_type || "未知",
|
||||||
participants: game.participants || [],
|
participants: game.participants || [],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -210,8 +231,12 @@ export class UserService {
|
|||||||
|
|
||||||
private static is_date_in_this_week(date: Date): boolean {
|
private static is_date_in_this_week(date: Date): boolean {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const firstDayOfWeek = new Date(today.setDate(today.getDate() - today.getDay()));
|
const firstDayOfWeek = new Date(
|
||||||
const lastDayOfWeek = new Date(firstDayOfWeek.setDate(firstDayOfWeek.getDate() + 6));
|
today.setDate(today.getDate() - today.getDay())
|
||||||
|
);
|
||||||
|
const lastDayOfWeek = new Date(
|
||||||
|
firstDayOfWeek.setDate(firstDayOfWeek.getDate() + 6)
|
||||||
|
);
|
||||||
|
|
||||||
return date >= firstDayOfWeek && date <= lastDayOfWeek;
|
return date >= firstDayOfWeek && date <= lastDayOfWeek;
|
||||||
}
|
}
|
||||||
@@ -219,17 +244,27 @@ export class UserService {
|
|||||||
// 格式化时间显示
|
// 格式化时间显示
|
||||||
private static format_date_time(start_time: Date): string {
|
private static format_date_time(start_time: Date): string {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const today = new Date(now.getFullYear(), now.getMonth() + 1, now.getDate());
|
const today = new Date(
|
||||||
|
now.getFullYear(),
|
||||||
|
now.getMonth() + 1,
|
||||||
|
now.getDate()
|
||||||
|
);
|
||||||
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
|
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
|
||||||
const day_after_tomorrow = new Date(today.getTime() + 2 * 24 * 60 * 60 * 1000);
|
const day_after_tomorrow = new Date(
|
||||||
const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
today.getTime() + 2 * 24 * 60 * 60 * 1000
|
||||||
|
);
|
||||||
|
const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
||||||
|
|
||||||
const start_date = new Date(start_time.getFullYear(), start_time.getMonth() + 1, start_time.getDate());
|
const start_date = new Date(
|
||||||
|
start_time.getFullYear(),
|
||||||
|
start_time.getMonth() + 1,
|
||||||
|
start_time.getDate()
|
||||||
|
);
|
||||||
const weekday = weekdays[start_time.getDay()];
|
const weekday = weekdays[start_time.getDay()];
|
||||||
|
|
||||||
let date_str = '';
|
let date_str = "";
|
||||||
if (start_date.getTime() === today.getTime()) {
|
if (start_date.getTime() === today.getTime()) {
|
||||||
date_str = '今天';
|
date_str = "今天";
|
||||||
} else if (start_date.getTime() === tomorrow.getTime()) {
|
} else if (start_date.getTime() === tomorrow.getTime()) {
|
||||||
date_str = `明天(${weekday})`;
|
date_str = `明天(${weekday})`;
|
||||||
} else if (start_date.getTime() === day_after_tomorrow.getTime()) {
|
} else if (start_date.getTime() === day_after_tomorrow.getTime()) {
|
||||||
@@ -237,63 +272,84 @@ export class UserService {
|
|||||||
} else if (this.is_date_in_this_week(start_time)) {
|
} else if (this.is_date_in_this_week(start_time)) {
|
||||||
date_str = weekday;
|
date_str = weekday;
|
||||||
} else {
|
} else {
|
||||||
date_str = `${start_time.getFullYear()}-${(start_time.getMonth() + 1).toString().padStart(2, '0')}-${start_time.getDate().toString().padStart(2, '0')}(${weekday})`;
|
date_str = `${start_time.getFullYear()}-${(start_time.getMonth() + 1)
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}-${start_time
|
||||||
|
.getDate()
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}(${weekday})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const time_str = `${start_time.getHours().toString().padStart(2, '0')}:${start_time.getMinutes().toString().padStart(2, '0')}`;
|
const time_str = `${start_time
|
||||||
|
.getHours()
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}:${start_time
|
||||||
|
.getMinutes()
|
||||||
|
.toString()
|
||||||
|
.padStart(2, "0")}`;
|
||||||
return `${date_str} ${time_str}`;
|
return `${date_str} ${time_str}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算距离(模拟实现,实际需要根据用户位置计算)
|
// 计算距离(模拟实现,实际需要根据用户位置计算)
|
||||||
private static calculate_distance(latitude: number, longitude: number): string {
|
private static calculate_distance(
|
||||||
|
latitude: number,
|
||||||
|
longitude: number
|
||||||
|
): string {
|
||||||
if (latitude === 0 && longitude === 0) {
|
if (latitude === 0 && longitude === 0) {
|
||||||
return '未知距离';
|
return "未知距离";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 这里应该根据用户当前位置计算实际距离
|
// 这里应该根据用户当前位置计算实际距离
|
||||||
// 暂时返回模拟距离
|
// 暂时返回模拟距离
|
||||||
const distances = ['1.2km', '2.5km', '3.8km', '5.1km', '7.3km'];
|
const distances = ["1.2km", "2.5km", "3.8km", "5.1km", "7.3km"];
|
||||||
return distances[Math.floor(Math.random() * distances.length)];
|
return distances[Math.floor(Math.random() * distances.length)];
|
||||||
}
|
}
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
static async get_user_info(user_id?: string): Promise<UserInfo> {
|
static async get_user_info(user_id?: string): Promise<UserInfo> {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post<UserDetailData>(API_CONFIG.USER.DETAIL, user_id ? { user_id } : {}, {
|
const response = await httpService.post<UserDetailData>(
|
||||||
|
API_CONFIG.USER.DETAIL,
|
||||||
showLoading: false
|
user_id ? { user_id } : {},
|
||||||
});
|
{
|
||||||
|
showLoading: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
const userData = response.data;
|
const userData = response.data;
|
||||||
return {
|
return {
|
||||||
id: userData.id || '',
|
id: userData.id || "",
|
||||||
nickname: userData.nickname || '',
|
nickname: userData.nickname || "",
|
||||||
avatar: userData.avatar_url || '',
|
avatar: userData.avatar_url || "",
|
||||||
join_date: userData.subscribe_time ? `${new Date(userData.subscribe_time).getFullYear()}年${new Date(userData.subscribe_time).getMonth() + 1}月加入` : '',
|
join_date: userData.subscribe_time
|
||||||
|
? `${new Date(userData.subscribe_time).getFullYear()}年${
|
||||||
|
new Date(userData.subscribe_time).getMonth() + 1
|
||||||
|
}月加入`
|
||||||
|
: "",
|
||||||
stats: {
|
stats: {
|
||||||
following: userData.stats?.following_count || 0,
|
following: userData.stats?.following_count || 0,
|
||||||
friends: userData.stats?.followers_count || 0,
|
friends: userData.stats?.followers_count || 0,
|
||||||
hosted: userData.stats?.hosted_games_count || 0,
|
hosted: userData.stats?.hosted_games_count || 0,
|
||||||
participated: userData.stats?.participated_games_count || 0
|
participated: userData.stats?.participated_games_count || 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
personal_profile: userData.personal_profile || '',
|
personal_profile: userData.personal_profile || "",
|
||||||
occupation: userData.occupation || '',
|
occupation: userData.occupation || "",
|
||||||
ntrp_level: userData.ntrp_level || '',
|
ntrp_level: userData.ntrp_level || "",
|
||||||
phone: userData.phone || '',
|
phone: userData.phone || "",
|
||||||
gender: userData.gender || '',
|
gender: userData.gender || "",
|
||||||
birthday: userData.birthday || '',
|
birthday: userData.birthday || "",
|
||||||
country: userData.country || '',
|
country: userData.country || "",
|
||||||
province: userData.province || '',
|
province: userData.province || "",
|
||||||
city: userData.city || '',
|
city: userData.city || "",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '获取用户信息失败');
|
throw new Error(response.message || "获取用户信息失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取用户信息失败:', error);
|
console.error("获取用户信息失败:", error);
|
||||||
// 返回默认用户信息
|
// 返回默认用户信息
|
||||||
return {} as UserInfo
|
return {} as UserInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,32 +359,36 @@ export class UserService {
|
|||||||
// 过滤掉空字段
|
// 过滤掉空字段
|
||||||
const filtered_data: Record<string, any> = {};
|
const filtered_data: Record<string, any> = {};
|
||||||
|
|
||||||
Object.keys(update_data).forEach(key => {
|
Object.keys(update_data).forEach((key) => {
|
||||||
const value = update_data[key as keyof UserInfo];
|
const value = update_data[key as keyof UserInfo];
|
||||||
// 只添加非空且非空字符串的字段
|
// 只添加非空且非空字符串的字段
|
||||||
if (value !== null && value !== undefined && value !== '') {
|
if (value !== null && value !== undefined && value !== "") {
|
||||||
if (typeof value === 'string' && value.trim() !== '') {
|
if (typeof value === "string" && value.trim() !== "") {
|
||||||
filtered_data[key] = value.trim();
|
filtered_data[key] = value.trim();
|
||||||
} else if (typeof value !== 'string') {
|
} else if (typeof value !== "string") {
|
||||||
filtered_data[key] = value;
|
filtered_data[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 如果没有需要更新的字段,直接返回
|
// 如果没有需要更新的字段,直接返回
|
||||||
if (Object.keys(filtered_data).length === 0) {
|
if (Object.keys(filtered_data).length === 0) {
|
||||||
console.log('没有需要更新的字段');
|
console.log("没有需要更新的字段");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await httpService.post(API_CONFIG.USER.UPDATE, filtered_data, {
|
const response = await httpService.post(
|
||||||
showLoading: true
|
API_CONFIG.USER.UPDATE,
|
||||||
});
|
filtered_data,
|
||||||
|
{
|
||||||
|
showLoading: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (response.code !== 0) {
|
if (response.code !== 0) {
|
||||||
throw new Error(response.message || '更新用户信息失败');
|
throw new Error(response.message || "更新用户信息失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新用户信息失败:', error);
|
console.error("更新用户信息失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,53 +396,61 @@ export class UserService {
|
|||||||
// 获取用户主办的球局
|
// 获取用户主办的球局
|
||||||
static async get_hosted_games(userId: string | number): Promise<any[]> {
|
static async get_hosted_games(userId: string | number): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post<any>(API_CONFIG.USER.HOSTED_GAMES, {
|
const response = await httpService.post<any>(
|
||||||
userId
|
API_CONFIG.USER.HOSTED_GAMES,
|
||||||
}, {
|
{
|
||||||
|
userId,
|
||||||
showLoading: false
|
},
|
||||||
});
|
{
|
||||||
|
showLoading: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
// 使用数据转换函数将后端数据转换为ListContainer期望的格式
|
// 使用数据转换函数将后端数据转换为ListContainer期望的格式
|
||||||
return this.transform_game_data(response.data.rows || []);
|
return this.transform_game_data(response.data.rows || []);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '获取主办球局失败');
|
throw new Error(response.message || "获取主办球局失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取主办球局失败:', error);
|
console.error("获取主办球局失败:", error);
|
||||||
// 返回符合ListContainer data格式的模拟数据
|
// 返回符合ListContainer data格式的模拟数据
|
||||||
return []
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户参与的球局
|
// 获取用户参与的球局
|
||||||
static async get_participated_games(userId: string | number): Promise<any[]> {
|
static async get_participated_games(userId: string | number): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post<any>(API_CONFIG.USER.PARTICIPATED_GAMES, {
|
const response = await httpService.post<any>(
|
||||||
userId
|
API_CONFIG.USER.PARTICIPATED_GAMES,
|
||||||
}, {
|
{
|
||||||
|
userId,
|
||||||
showLoading: false
|
},
|
||||||
});
|
{
|
||||||
|
showLoading: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
// 使用数据转换函数将后端数据转换为ListContainer期望的格式
|
// 使用数据转换函数将后端数据转换为ListContainer期望的格式
|
||||||
return this.transform_game_data(response.data.rows || []);
|
return this.transform_game_data(response.data.rows || []);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '获取参与球局失败');
|
throw new Error(response.message || "获取参与球局失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取参与球局失败:', error);
|
console.error("获取参与球局失败:", error);
|
||||||
// 返回符合ListContainer data格式的模拟数据
|
// 返回符合ListContainer data格式的模拟数据
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户球局记录(兼容旧方法)
|
// 获取用户球局记录(兼容旧方法)
|
||||||
static async get_user_games(user_id: string | number, type: 'hosted' | 'participated'): Promise<any[]> {
|
static async get_user_games(
|
||||||
if (type === 'hosted') {
|
user_id: string | number,
|
||||||
|
type: "hosted" | "participated"
|
||||||
|
): Promise<any[]> {
|
||||||
|
if (type === "hosted") {
|
||||||
return this.get_hosted_games(user_id);
|
return this.get_hosted_games(user_id);
|
||||||
} else {
|
} else {
|
||||||
return this.get_participated_games(user_id);
|
return this.get_participated_games(user_id);
|
||||||
@@ -390,98 +458,118 @@ export class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 关注/取消关注用户
|
// 关注/取消关注用户
|
||||||
static async toggle_follow(following_id: string | number, is_following: boolean): Promise<boolean> {
|
static async toggle_follow(
|
||||||
|
following_id: string | number,
|
||||||
|
is_following: boolean
|
||||||
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const endpoint = is_following ? API_CONFIG.USER.UNFOLLOW : API_CONFIG.USER.FOLLOW;
|
const endpoint = is_following
|
||||||
const response = await httpService.post<any>(endpoint, { following_id }, {
|
? API_CONFIG.USER.UNFOLLOW
|
||||||
|
: API_CONFIG.USER.FOLLOW;
|
||||||
|
const response = await httpService.post<any>(
|
||||||
|
endpoint,
|
||||||
|
{ following_id },
|
||||||
|
{
|
||||||
showLoading: true,
|
showLoading: true,
|
||||||
loadingText: is_following ? '取消关注中...' : '关注中...'
|
loadingText: is_following ? "取消关注中..." : "关注中...",
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
return !is_following;
|
return !is_following;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '操作失败');
|
throw new Error(response.message || "操作失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('关注操作失败:', error);
|
console.error("关注操作失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存用户信息
|
// 保存用户信息
|
||||||
static async save_user_info(user_info: Partial<UserInfo> & { phone?: string; gender?: string }): Promise<boolean> {
|
static async save_user_info(
|
||||||
|
user_info: Partial<UserInfo> & { phone?: string; gender?: string }
|
||||||
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// 字段映射配置
|
// 字段映射配置
|
||||||
const field_mapping: Record<string, string> = {
|
const field_mapping: Record<string, string> = {
|
||||||
nickname: 'nickname',
|
nickname: "nickname",
|
||||||
avatar: 'avatar_url',
|
avatar: "avatar_url",
|
||||||
gender: 'gender',
|
gender: "gender",
|
||||||
phone: 'phone',
|
phone: "phone",
|
||||||
latitude: 'latitude',
|
latitude: "latitude",
|
||||||
longitude: 'longitude',
|
longitude: "longitude",
|
||||||
province: 'province',
|
province: "province",
|
||||||
country: "country",
|
country: "country",
|
||||||
city: "city",
|
city: "city",
|
||||||
personal_profile: 'personal_profile',
|
personal_profile: "personal_profile",
|
||||||
occupation: 'occupation',
|
occupation: "occupation",
|
||||||
ntrp_level: 'ntrp_level'
|
ntrp_level: "ntrp_level",
|
||||||
};
|
};
|
||||||
|
|
||||||
// 构建更新参数,只包含非空字段
|
// 构建更新参数,只包含非空字段
|
||||||
const updateParams: Record<string, string> = {};
|
const updateParams: Record<string, string> = {};
|
||||||
|
|
||||||
// 循环处理所有字段
|
// 循环处理所有字段
|
||||||
Object.keys(field_mapping).forEach(key => {
|
Object.keys(field_mapping).forEach((key) => {
|
||||||
const value = user_info[key as keyof typeof user_info];
|
const value = user_info[key as keyof typeof user_info];
|
||||||
if (value && typeof value === 'string' && value.trim() !== '') {
|
if (value && typeof value === "string" && value.trim() !== "") {
|
||||||
updateParams[field_mapping[key]] = value.trim();
|
updateParams[field_mapping[key]] = value.trim();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// 如果没有需要更新的字段,直接返回成功
|
// 如果没有需要更新的字段,直接返回成功
|
||||||
if (Object.keys(updateParams).length === 0) {
|
if (Object.keys(updateParams).length === 0) {
|
||||||
console.log('没有需要更新的字段');
|
console.log("没有需要更新的字段");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await httpService.post<any>(API_CONFIG.USER.UPDATE, updateParams, {
|
const response = await httpService.post<any>(
|
||||||
|
API_CONFIG.USER.UPDATE,
|
||||||
|
updateParams,
|
||||||
|
{
|
||||||
showLoading: true,
|
showLoading: true,
|
||||||
loadingText: '保存中...'
|
loadingText: "保存中...",
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '更新用户信息失败');
|
throw new Error(response.message || "更新用户信息失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存用户信息失败:', error);
|
console.error("保存用户信息失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户动态
|
// 获取用户动态
|
||||||
static async get_user_activities(user_id: string, page: number = 1, limit: number = 10): Promise<any[]> {
|
static async get_user_activities(
|
||||||
|
user_id: string,
|
||||||
|
page: number = 1,
|
||||||
|
limit: number = 10
|
||||||
|
): Promise<any[]> {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post<any>('/user/activities', {
|
const response = await httpService.post<any>(
|
||||||
|
"/user/activities",
|
||||||
|
{
|
||||||
user_id,
|
user_id,
|
||||||
page,
|
page,
|
||||||
limit
|
limit,
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
showLoading: false
|
showLoading: false,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
return response.data.activities || [];
|
return response.data.activities || [];
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '获取用户动态失败');
|
throw new Error(response.message || "获取用户动态失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取用户动态失败:', error);
|
console.error("获取用户动态失败:", error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -490,34 +578,38 @@ export class UserService {
|
|||||||
static async upload_avatar(file_path: string): Promise<string> {
|
static async upload_avatar(file_path: string): Promise<string> {
|
||||||
try {
|
try {
|
||||||
// 先上传文件到服务器
|
// 先上传文件到服务器
|
||||||
const result = await uploadFiles.upload_oss_img(file_path)
|
const result = await uploadFiles.upload_oss_img(file_path);
|
||||||
|
|
||||||
await this.save_user_info({ avatar: result.ossPath })
|
await this.save_user_info({ avatar: result.ossPath });
|
||||||
|
|
||||||
// 使用新的响应格式中的file_url字段
|
// 使用新的响应格式中的file_url字段
|
||||||
return result.ossPath;
|
return result.ossPath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('头像上传失败:', error);
|
console.error("头像上传失败:", error);
|
||||||
// 如果上传失败,返回默认头像
|
// 如果上传失败,返回默认头像
|
||||||
return require('../static/userInfo/default_avatar.svg');
|
return require("../static/userInfo/default_avatar.svg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析用户手机号
|
// 解析用户手机号
|
||||||
static async parse_phone(phone_code: string): Promise<string> {
|
static async parse_phone(phone_code: string): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post<{ phone: string }>(API_CONFIG.USER.PARSE_PHONE, { phone_code }, {
|
const response = await httpService.post<{ phone: string }>(
|
||||||
|
API_CONFIG.USER.PARSE_PHONE,
|
||||||
|
{ phone_code },
|
||||||
|
{
|
||||||
showLoading: true,
|
showLoading: true,
|
||||||
loadingText: '获取手机号中...'
|
loadingText: "获取手机号中...",
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
return response.data.phone || '';
|
return response.data.phone || "";
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '获取手机号失败');
|
throw new Error(response.message || "获取手机号失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取手机号失败:', error);
|
console.error("获取手机号失败:", error);
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,10 +621,10 @@ export class UserService {
|
|||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
return formatOptions(data || []);
|
return formatOptions(data || []);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(message || '获取职业树失败');
|
throw new Error(message || "获取职业树失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取职业树失败:', error);
|
console.error("获取职业树失败:", error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,10 +637,10 @@ export class UserService {
|
|||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
return formatOptions(data || []);
|
return formatOptions(data || []);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(message || '获取城市树失败');
|
throw new Error(message || "获取城市树失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取职业树失败:', error);
|
console.error("获取职业树失败:", error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -566,10 +658,10 @@ export class UserService {
|
|||||||
url: "/login_pages/index/index",
|
url: "/login_pages/index/index",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error(message || '注销账户失败');
|
throw new Error(message || "注销账户失败");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('注销账户失败:', error);
|
console.error("注销账户失败:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -577,12 +669,14 @@ export class UserService {
|
|||||||
// 从 loginService 移过来的用户相关方法
|
// 从 loginService 移过来的用户相关方法
|
||||||
|
|
||||||
// 获取用户详细信息
|
// 获取用户详细信息
|
||||||
export const fetchUserProfile = async (): Promise<ApiResponse<UserInfoType>> => {
|
export const fetchUserProfile = async (): Promise<
|
||||||
|
ApiResponse<UserInfoType>
|
||||||
|
> => {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post('user/detail');
|
const response = await httpService.post("user/detail");
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取用户信息失败:', error);
|
console.error("获取用户信息失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -590,24 +684,27 @@ export const fetchUserProfile = async (): Promise<ApiResponse<UserInfoType>> =>
|
|||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
export const updateUserProfile = async (payload: Partial<UserInfoType>) => {
|
export const updateUserProfile = async (payload: Partial<UserInfoType>) => {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post('/user/update', payload);
|
const response = await httpService.post("/user/update", payload);
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新用户信息失败:', error);
|
console.error("更新用户信息失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新用户坐标位置
|
// 更新用户坐标位置
|
||||||
export const updateUserLocation = async (latitude: number, longitude: number) => {
|
export const updateUserLocation = async (
|
||||||
|
latitude: number,
|
||||||
|
longitude: number
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const response = await httpService.post('/user/update_location', {
|
const response = await httpService.post("/user/update_location", {
|
||||||
latitude,
|
latitude,
|
||||||
longitude,
|
longitude,
|
||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新用户坐标位置失败:', error);
|
console.error("更新用户坐标位置失败:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -615,9 +712,9 @@ export const updateUserLocation = async (latitude: number, longitude: number) =>
|
|||||||
// 获取用户信息(从本地存储)
|
// 获取用户信息(从本地存储)
|
||||||
export const get_user_info = (): any | null => {
|
export const get_user_info = (): any | null => {
|
||||||
try {
|
try {
|
||||||
let userinfo = Taro.getStorageSync('user_info')
|
let userinfo = Taro.getStorageSync("user_info");
|
||||||
if (userinfo) {
|
if (userinfo) {
|
||||||
return JSON.parse(userinfo)
|
return JSON.parse(userinfo);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -632,25 +729,25 @@ export const handleCustomerService = async (): Promise<void> => {
|
|||||||
const config = getCurrentConfig;
|
const config = getCurrentConfig;
|
||||||
const { customerService } = config;
|
const { customerService } = config;
|
||||||
|
|
||||||
console.log('打开客服中心,配置信息:', customerService);
|
console.log("打开客服中心,配置信息:", customerService);
|
||||||
|
|
||||||
// 使用微信官方客服能力
|
// 使用微信官方客服能力
|
||||||
await Taro.openCustomerServiceChat({
|
await Taro.openCustomerServiceChat({
|
||||||
extInfo: {
|
extInfo: {
|
||||||
url: customerService.serviceUrl
|
url: customerService.serviceUrl,
|
||||||
},
|
},
|
||||||
corpId: customerService.corpId,
|
corpId: customerService.corpId,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
console.log('打开客服成功:', res);
|
console.log("打开客服成功:", res);
|
||||||
},
|
},
|
||||||
fail: (error) => {
|
fail: (error) => {
|
||||||
console.error('打开客服失败:', error);
|
console.error("打开客服失败:", error);
|
||||||
// 如果官方客服不可用,显示备用联系方式
|
// 如果官方客服不可用,显示备用联系方式
|
||||||
showCustomerServiceFallback(customerService);
|
showCustomerServiceFallback(customerService);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('客服功能异常:', error);
|
console.error("客服功能异常:", error);
|
||||||
// 备用方案:显示联系信息
|
// 备用方案:显示联系信息
|
||||||
showCustomerServiceFallback();
|
showCustomerServiceFallback();
|
||||||
}
|
}
|
||||||
@@ -658,14 +755,14 @@ export const handleCustomerService = async (): Promise<void> => {
|
|||||||
|
|
||||||
// 客服备用方案
|
// 客服备用方案
|
||||||
const showCustomerServiceFallback = (customerInfo?: any) => {
|
const showCustomerServiceFallback = (customerInfo?: any) => {
|
||||||
const options = ['拨打客服电话', '复制邮箱地址'];
|
const options = ["拨打客服电话", "复制邮箱地址"];
|
||||||
|
|
||||||
// 如果没有客服信息,只显示通用提示
|
// 如果没有客服信息,只显示通用提示
|
||||||
if (!customerInfo?.phoneNumber && !customerInfo?.email) {
|
if (!customerInfo?.phoneNumber && !customerInfo?.email) {
|
||||||
Taro.showModal({
|
Taro.showModal({
|
||||||
title: '联系客服',
|
title: "联系客服",
|
||||||
content: '如需帮助,请通过其他方式联系我们',
|
content: "如需帮助,请通过其他方式联系我们",
|
||||||
showCancel: false
|
showCancel: false,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -677,33 +774,33 @@ const showCustomerServiceFallback = (customerInfo?: any) => {
|
|||||||
// 拨打客服电话
|
// 拨打客服电话
|
||||||
try {
|
try {
|
||||||
await Taro.makePhoneCall({
|
await Taro.makePhoneCall({
|
||||||
phoneNumber: customerInfo.phoneNumber
|
phoneNumber: customerInfo.phoneNumber,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('拨打电话失败:', error);
|
console.error("拨打电话失败:", error);
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '拨打电话失败',
|
title: "拨打电话失败",
|
||||||
icon: 'none'
|
icon: "none",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (res.tapIndex === 1 && customerInfo?.email) {
|
} else if (res.tapIndex === 1 && customerInfo?.email) {
|
||||||
// 复制邮箱地址
|
// 复制邮箱地址
|
||||||
try {
|
try {
|
||||||
await Taro.setClipboardData({
|
await Taro.setClipboardData({
|
||||||
data: customerInfo.email
|
data: customerInfo.email,
|
||||||
});
|
});
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '邮箱地址已复制',
|
title: "邮箱地址已复制",
|
||||||
icon: 'success'
|
icon: "success",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('复制邮箱失败:', error);
|
console.error("复制邮箱失败:", error);
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '复制失败',
|
title: "复制失败",
|
||||||
icon: 'none'
|
icon: "none",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,18 +11,31 @@ export interface UserState {
|
|||||||
updateUserInfo: (userInfo: Partial<UserInfoType>) => void;
|
updateUserInfo: (userInfo: Partial<UserInfoType>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUser = create<UserState>()((set) => ({
|
const fetchUserInfo = async (set) => {
|
||||||
user: {},
|
|
||||||
fetchUserInfo: async () => {
|
|
||||||
try {
|
try {
|
||||||
const res = await fetchUserProfile();
|
const res = await fetchUserProfile();
|
||||||
set({ user: res.data });
|
set({ user: res.data });
|
||||||
return res.data
|
return res.data;
|
||||||
} catch {}
|
} catch {}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
export const useUser = create<UserState>()((set) => ({
|
||||||
|
user: {},
|
||||||
|
fetchUserInfo: fetchUserInfo.bind(null, set),
|
||||||
updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
|
updateUserInfo: async (userInfo: Partial<UserInfoType>) => {
|
||||||
const res = await updateUserProfile(userInfo);
|
try {
|
||||||
set({ user: res.data });
|
// 先更新后端
|
||||||
|
await updateUserProfile(userInfo);
|
||||||
|
// 然后立即更新本地状态
|
||||||
|
set((state) => ({
|
||||||
|
user: { ...state.user, ...userInfo },
|
||||||
|
}));
|
||||||
|
// 最后重新获取完整用户信息确保数据一致性
|
||||||
|
await fetchUserInfo(set);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更新用户信息失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -3,53 +3,36 @@ import { View, Text, Image, ScrollView, 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";
|
||||||
import { UserInfo } from "@/components/UserInfo";
|
|
||||||
import { UserService, PickerOption } from "@/services/userService";
|
import { UserService, PickerOption } from "@/services/userService";
|
||||||
import { clear_login_state } from "@/services/loginService";
|
import { clear_login_state } from "@/services/loginService";
|
||||||
import { convert_db_gender_to_display } from "@/utils/genderUtils";
|
import { convert_db_gender_to_display } from "@/utils/genderUtils";
|
||||||
import { EditModal } from "@/components";
|
import { EditModal } from "@/components";
|
||||||
import img from "@/config/images";
|
import img from "@/config/images";
|
||||||
import CommonDialog from "@/components/CommonDialog";
|
import CommonDialog from "@/components/CommonDialog";
|
||||||
|
import { useUserActions, useUserInfo } from "@/store/userStore";
|
||||||
|
import { UserInfoType } from "@/services/userService";
|
||||||
|
|
||||||
const EditProfilePage: React.FC = () => {
|
const EditProfilePage: React.FC = () => {
|
||||||
// 用户信息状态
|
const { updateUserInfo } = useUserActions();
|
||||||
const [user_info, setUserInfo] = useState<UserInfo>({
|
// 直接从store获取用户信息,确保响应式更新
|
||||||
id: "1",
|
const user_info = useUserInfo();
|
||||||
nickname: "加载中...",
|
|
||||||
avatar: require("@/static/userInfo/default_avatar.svg"),
|
|
||||||
join_date: "加载中...",
|
|
||||||
stats: {
|
|
||||||
following: 0,
|
|
||||||
friends: 0,
|
|
||||||
hosted: 0,
|
|
||||||
participated: 0,
|
|
||||||
},
|
|
||||||
personal_profile: "加载中...",
|
|
||||||
occupation: "加载中...",
|
|
||||||
ntrp_level: "NTRP 3.0",
|
|
||||||
phone: "",
|
|
||||||
gender: "",
|
|
||||||
country: "",
|
|
||||||
province: "",
|
|
||||||
city: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 表单状态
|
// 表单状态,基于store中的用户信息初始化
|
||||||
const [form_data, setFormData] = useState({
|
const [form_data, setFormData] = useState({
|
||||||
nickname: "",
|
nickname: (user_info as UserInfoType)?.nickname || "",
|
||||||
personal_profile: "",
|
personal_profile: (user_info as UserInfoType)?.personal_profile || "",
|
||||||
occupation: "",
|
occupation: (user_info as UserInfoType)?.occupation || "",
|
||||||
ntrp_level: "4.0",
|
ntrp_level: (user_info as UserInfoType)?.ntrp_level || "4.0",
|
||||||
phone: "",
|
phone: (user_info as UserInfoType)?.phone || "",
|
||||||
gender: "",
|
gender: (user_info as UserInfoType)?.gender || "",
|
||||||
birthday: "2000-01-01",
|
birthday: (user_info as UserInfoType)?.birthday || "2000-01-01",
|
||||||
country: "",
|
country: (user_info as UserInfoType)?.country || "",
|
||||||
province: "",
|
province: (user_info as UserInfoType)?.province || "",
|
||||||
city: "",
|
city: (user_info as UserInfoType)?.city || "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// 加载状态
|
// 加载状态
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(false);
|
||||||
const [showLogoutDialog, setShowLogoutDialog] = useState(false);
|
const [showLogoutDialog, setShowLogoutDialog] = useState(false);
|
||||||
|
|
||||||
// 编辑弹窗状态
|
// 编辑弹窗状态
|
||||||
@@ -68,11 +51,28 @@ const EditProfilePage: React.FC = () => {
|
|||||||
// 城市数据
|
// 城市数据
|
||||||
const [cities, setCities] = useState<PickerOption[]>([]);
|
const [cities, setCities] = useState<PickerOption[]>([]);
|
||||||
|
|
||||||
|
// 监听store中的用户信息变化,同步到表单状态
|
||||||
|
useEffect(() => {
|
||||||
|
if (user_info && Object.keys(user_info).length > 0) {
|
||||||
|
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 || "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [user_info]);
|
||||||
|
|
||||||
// 页面加载时初始化数据
|
// 页面加载时初始化数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
load_user_info();
|
|
||||||
getProfessions();
|
|
||||||
getCities();
|
getCities();
|
||||||
|
getProfessions();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getProfessions = async () => {
|
const getProfessions = async () => {
|
||||||
@@ -93,34 +93,34 @@ const EditProfilePage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 加载用户信息
|
// 加载用户信息
|
||||||
const load_user_info = async () => {
|
// const load_user_info = async () => {
|
||||||
try {
|
// try {
|
||||||
setLoading(true);
|
// setLoading(true);
|
||||||
const user_data = await UserService.get_user_info();
|
// const user_data = await UserService.get_user_info();
|
||||||
setUserInfo(user_data);
|
// setUserInfo(user_data);
|
||||||
setFormData({
|
// setFormData({
|
||||||
nickname: user_data.nickname || "",
|
// nickname: user_data.nickname || "",
|
||||||
personal_profile: user_data.personal_profile || "",
|
// personal_profile: user_data.personal_profile || "",
|
||||||
occupation: user_data.occupation || "",
|
// occupation: user_data.occupation || "",
|
||||||
ntrp_level: user_data.ntrp_level || "NTRP 4.0",
|
// ntrp_level: user_data.ntrp_level || "NTRP 4.0",
|
||||||
phone: user_data.phone || "",
|
// phone: user_data.phone || "",
|
||||||
gender: user_data.gender || "",
|
// gender: user_data.gender || "",
|
||||||
birthday: user_data.birthday || "",
|
// birthday: user_data.birthday || "",
|
||||||
country: user_data.country || "",
|
// country: user_data.country || "",
|
||||||
province: user_data.province || "",
|
// province: user_data.province || "",
|
||||||
city: user_data.city || "",
|
// city: user_data.city || "",
|
||||||
});
|
// });
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error("加载用户信息失败:", error);
|
// console.error("加载用户信息失败:", error);
|
||||||
Taro.showToast({
|
// Taro.showToast({
|
||||||
title: "加载用户信息失败",
|
// title: "加载用户信息失败",
|
||||||
icon: "error",
|
// icon: "error",
|
||||||
duration: 2000,
|
// duration: 2000,
|
||||||
});
|
// });
|
||||||
} finally {
|
// } finally {
|
||||||
setLoading(false);
|
// setLoading(false);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
// 处理头像上传
|
// 处理头像上传
|
||||||
const handle_avatar_upload = () => {
|
const handle_avatar_upload = () => {
|
||||||
@@ -184,11 +184,10 @@ const EditProfilePage: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
// 调用更新用户信息接口,只传递修改的字段
|
// 调用更新用户信息接口,只传递修改的字段
|
||||||
const update_data = { [editing_field]: value };
|
const update_data = { [editing_field]: value };
|
||||||
await UserService.update_user_info(update_data);
|
await updateUserInfo(update_data);
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新表单状态(store会自动更新)
|
||||||
setFormData((prev) => ({ ...prev, [editing_field]: value }));
|
setFormData((prev) => ({ ...prev, [editing_field]: value }));
|
||||||
setUserInfo((prev) => ({ ...prev, [editing_field]: value }));
|
|
||||||
|
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
setEditModalVisible(false);
|
setEditModalVisible(false);
|
||||||
@@ -224,20 +223,18 @@ const EditProfilePage: React.FC = () => {
|
|||||||
field !== null &&
|
field !== null &&
|
||||||
!Array.isArray(field)
|
!Array.isArray(field)
|
||||||
) {
|
) {
|
||||||
await UserService.update_user_info({ ...field });
|
await updateUserInfo({ ...field });
|
||||||
// 更新本地状态
|
|
||||||
|
// 更新表单状态(store会自动更新)
|
||||||
setFormData((prev) => ({ ...prev, ...field }));
|
setFormData((prev) => ({ ...prev, ...field }));
|
||||||
setUserInfo((prev) => ({ ...prev, ...field }));
|
|
||||||
} else {
|
} else {
|
||||||
// 调用更新用户信息接口,只传递修改的字段
|
// 调用更新用户信息接口,只传递修改的字段
|
||||||
const update_data = { [field as string]: value };
|
const update_data = { [field as string]: value };
|
||||||
await UserService.update_user_info(update_data);
|
await updateUserInfo(update_data);
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新表单状态(store会自动更新)
|
||||||
setFormData((prev) => ({ ...prev, [field as string]: value }));
|
setFormData((prev) => ({ ...prev, [field as string]: value }));
|
||||||
setUserInfo((prev) => ({ ...prev, [field as string]: value }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示成功提示
|
// 显示成功提示
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: "保存成功",
|
title: "保存成功",
|
||||||
@@ -364,7 +361,7 @@ const EditProfilePage: React.FC = () => {
|
|||||||
<View className="avatar_container" onClick={handle_avatar_upload}>
|
<View className="avatar_container" onClick={handle_avatar_upload}>
|
||||||
<Image
|
<Image
|
||||||
className="avatar"
|
className="avatar"
|
||||||
src={user_info.avatar}
|
src={(user_info as UserInfoType)?.avatar_url || ""}
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
<View className="avatar_overlay">
|
<View className="avatar_overlay">
|
||||||
@@ -393,7 +390,7 @@ const EditProfilePage: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
<View className="item_right">
|
<View className="item_right">
|
||||||
<Text className="item_value">
|
<Text className="item_value">
|
||||||
{form_data.nickname || "188的王晨"}
|
{form_data.nickname || ""}
|
||||||
</Text>
|
</Text>
|
||||||
<Image
|
<Image
|
||||||
className="arrow_icon"
|
className="arrow_icon"
|
||||||
@@ -648,6 +645,8 @@ const EditProfilePage: React.FC = () => {
|
|||||||
{/* 生日选择弹窗 */}
|
{/* 生日选择弹窗 */}
|
||||||
{birthday_picker_visible && (
|
{birthday_picker_visible && (
|
||||||
<PopupPicker
|
<PopupPicker
|
||||||
|
minYear={1970}
|
||||||
|
maxYear={new Date().getFullYear()}
|
||||||
showHeader={true}
|
showHeader={true}
|
||||||
title="选择生日"
|
title="选择生日"
|
||||||
confirmText="保存"
|
confirmText="保存"
|
||||||
@@ -693,7 +692,7 @@ const EditProfilePage: React.FC = () => {
|
|||||||
],
|
],
|
||||||
]}
|
]}
|
||||||
type="ntrp"
|
type="ntrp"
|
||||||
img={user_info.avatar}
|
img={(user_info as UserInfoType)?.avatar_url}
|
||||||
visible={ntrp_picker_visible}
|
visible={ntrp_picker_visible}
|
||||||
setvisible={setNtrpPickerVisible}
|
setvisible={setNtrpPickerVisible}
|
||||||
value={[form_data.ntrp_level]}
|
value={[form_data.ntrp_level]}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ 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";
|
||||||
import GuideBar from "@/components/GuideBar";
|
import GuideBar from "@/components/GuideBar";
|
||||||
import { UserInfoCard, UserInfo } from "@/components/UserInfo/index";
|
import { UserInfoCard } from "@/components/UserInfo/index";
|
||||||
import { UserService } from "@/services/userService";
|
import { UserService, UserInfo } from "@/services/userService";
|
||||||
import ListContainer from "@/container/listContainer";
|
import ListContainer from "@/container/listContainer";
|
||||||
import { TennisMatch } from "@/../types/list/types";
|
import { TennisMatch } from "@/../types/list/types";
|
||||||
import { withAuth, NTRPTestEntryCard } from "@/components";
|
import { withAuth, NTRPTestEntryCard } from "@/components";
|
||||||
import { EvaluateScene } from "@/store/evaluateStore";
|
import { EvaluateScene } from "@/store/evaluateStore";
|
||||||
|
import { useUserInfo } from "@/store/userStore";
|
||||||
|
import { UserInfoType } from "@/services/userService";
|
||||||
|
|
||||||
const MyselfPage: React.FC = () => {
|
const MyselfPage: React.FC = () => {
|
||||||
// 获取页面参数
|
// 获取页面参数
|
||||||
@@ -18,33 +20,14 @@ const MyselfPage: React.FC = () => {
|
|||||||
// 判断是否为当前用户
|
// 判断是否为当前用户
|
||||||
const is_current_user = !user_id;
|
const is_current_user = !user_id;
|
||||||
|
|
||||||
// 用户信息状态
|
// 直接从store获取用户信息,确保响应式更新
|
||||||
const [user_info, set_user_info] = useState<UserInfo>({
|
const user_info = useUserInfo();
|
||||||
id: "",
|
|
||||||
nickname: "加载中...",
|
|
||||||
avatar: require("@/static/userInfo/default_avatar.svg"),
|
|
||||||
join_date: "加载中...",
|
|
||||||
stats: {
|
|
||||||
following: 0,
|
|
||||||
friends: 0,
|
|
||||||
hosted: 0,
|
|
||||||
participated: 0,
|
|
||||||
},
|
|
||||||
bio: "加载中...",
|
|
||||||
city: "加载中...",
|
|
||||||
occupation: "加载中...",
|
|
||||||
ntrp_level: "NTRP 3.0",
|
|
||||||
personal_profile: "加载中...",
|
|
||||||
gender: "",
|
|
||||||
country: "",
|
|
||||||
province: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 球局记录状态
|
// 球局记录状态
|
||||||
const [game_records, set_game_records] = useState<TennisMatch[]>([]);
|
const [game_records, set_game_records] = useState<TennisMatch[]>([]);
|
||||||
// 往期球局
|
// 往期球局
|
||||||
const [ended_game_records, setEndedGameRecords] = useState<TennisMatch[]>([]);
|
const [ended_game_records, setEndedGameRecords] = useState<TennisMatch[]>([]);
|
||||||
const [loading, set_loading] = useState(true);
|
const [loading, set_loading] = useState(false);
|
||||||
|
|
||||||
// 关注状态
|
// 关注状态
|
||||||
const [is_following, setIsFollowing] = useState(false);
|
const [is_following, setIsFollowing] = useState(false);
|
||||||
@@ -55,37 +38,37 @@ const MyselfPage: React.FC = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 加载用户数据
|
// 加载用户数据
|
||||||
const load_user_data = async () => {
|
// const load_user_data = async () => {
|
||||||
try {
|
// try {
|
||||||
set_loading(true);
|
// set_loading(true);
|
||||||
|
|
||||||
// 获取用户信息(包含统计数据)
|
// // 获取用户信息(包含统计数据)
|
||||||
const user_data = await UserService.get_user_info();
|
// const user_data = await UserService.get_user_info();
|
||||||
set_user_info(user_data);
|
// set_user_info(user_data);
|
||||||
// let games_data;
|
// // let games_data;
|
||||||
// if (active_tab === "hosted") {
|
// // if (active_tab === "hosted") {
|
||||||
// games_data = await UserService.get_hosted_games(user_id);
|
// // games_data = await UserService.get_hosted_games(user_id);
|
||||||
// } else {
|
// // } else {
|
||||||
// games_data = await UserService.get_participated_games(user_id);
|
// // games_data = await UserService.get_participated_games(user_id);
|
||||||
|
// // }
|
||||||
|
// // set_game_records(games_data);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("加载用户数据失败:", error);
|
||||||
|
// Taro.showToast({
|
||||||
|
// title: "加载失败,请重试",
|
||||||
|
// icon: "error",
|
||||||
|
// duration: 2000,
|
||||||
|
// });
|
||||||
|
// } finally {
|
||||||
|
// set_loading(false);
|
||||||
// }
|
// }
|
||||||
// set_game_records(games_data);
|
// };
|
||||||
} catch (error) {
|
|
||||||
console.error("加载用户数据失败:", error);
|
|
||||||
Taro.showToast({
|
|
||||||
title: "加载失败,请重试",
|
|
||||||
icon: "error",
|
|
||||||
duration: 2000,
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
set_loading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (user_info.id) {
|
// if (user_info.id) {
|
||||||
load_game_data(); // 在 user_info 更新后调用
|
// load_game_data(); // 在 user_info 更新后调用
|
||||||
}
|
// }
|
||||||
}, [user_info]);
|
// }, [user_info]);
|
||||||
|
|
||||||
// 页面加载时获取数据
|
// 页面加载时获取数据
|
||||||
// useEffect(() => {
|
// useEffect(() => {
|
||||||
@@ -93,7 +76,7 @@ const MyselfPage: React.FC = () => {
|
|||||||
// }, [user_id]);
|
// }, [user_id]);
|
||||||
|
|
||||||
useDidShow(() => {
|
useDidShow(() => {
|
||||||
load_user_data(); // 确保从编辑页面返回时刷新数据
|
// set_user_info(useUserInfo()); // 确保从编辑页面返回时刷新数据
|
||||||
});
|
});
|
||||||
|
|
||||||
// 切换标签页时重新加载球局数据
|
// 切换标签页时重新加载球局数据
|
||||||
@@ -196,19 +179,12 @@ const MyselfPage: React.FC = () => {
|
|||||||
<View className="main_content">
|
<View className="main_content">
|
||||||
{/* 用户信息区域 */}
|
{/* 用户信息区域 */}
|
||||||
<View className="user_info_section">
|
<View className="user_info_section">
|
||||||
{loading ? (
|
|
||||||
<View className="loading_container">
|
|
||||||
<Text className="loading_text">加载中...</Text>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<UserInfoCard
|
<UserInfoCard
|
||||||
user_info={user_info}
|
user_info={user_info}
|
||||||
is_current_user={is_current_user}
|
is_current_user={is_current_user}
|
||||||
is_following={is_following}
|
is_following={is_following}
|
||||||
on_follow={handle_follow}
|
on_follow={handle_follow}
|
||||||
set_user_info={set_user_info}
|
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{/* 球局订单和收藏功能 */}
|
{/* 球局订单和收藏功能 */}
|
||||||
<View className="quick_actions_section">
|
<View className="quick_actions_section">
|
||||||
<View className="action_card">
|
<View className="action_card">
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import {
|
|||||||
UserInfoCard,
|
UserInfoCard,
|
||||||
// GameCard,
|
// GameCard,
|
||||||
GameTabs,
|
GameTabs,
|
||||||
UserInfo,
|
// UserInfo,
|
||||||
GameRecord,
|
GameRecord,
|
||||||
} from "@/components/UserInfo";
|
} from "@/components/UserInfo";
|
||||||
import { UserService } from "@/services/userService";
|
import { UserService, UserInfoType } from "@/services/userService";
|
||||||
import * as LoginService from "@/services/loginService";
|
import * as LoginService from "@/services/loginService";
|
||||||
|
|
||||||
const OtherUserPage: React.FC = () => {
|
const OtherUserPage: React.FC = () => {
|
||||||
@@ -21,21 +21,22 @@ const OtherUserPage: React.FC = () => {
|
|||||||
const user_id = instance.router?.params?.userid;
|
const user_id = instance.router?.params?.userid;
|
||||||
|
|
||||||
// 模拟用户数据
|
// 模拟用户数据
|
||||||
const [user_info, setUserInfo] = useState<UserInfo>({
|
const [user_info, setUserInfo] = useState<Partial<UserInfoType>>({
|
||||||
id: user_id || "1",
|
id: parseInt(user_id || "1") || 1,
|
||||||
gender: "",
|
gender: "",
|
||||||
nickname: "网球爱好者",
|
nickname: "网球爱好者",
|
||||||
avatar: require("@/static/userInfo/default_avatar.svg"),
|
avatar_url: require("@/static/userInfo/default_avatar.svg"),
|
||||||
join_date: "2024年3月加入",
|
join_date: "2024年3月加入",
|
||||||
stats: {
|
stats: {
|
||||||
following: 0,
|
following_count: 0,
|
||||||
friends: 0,
|
followers_count: 0,
|
||||||
hosted: 0,
|
hosted_games_count: 0,
|
||||||
participated: 0,
|
participated_games_count: 0,
|
||||||
},
|
},
|
||||||
tags: ["北京朝阳", "金融从业者", "NTRP 3.5"],
|
tags: ["北京朝阳", "金融从业者", "NTRP 3.5"],
|
||||||
bio: "热爱网球的金融从业者,周末喜欢约球\n技术还在提升中,欢迎一起切磋\n平时在朝阳公园附近活动",
|
bio: "热爱网球的金融从业者,周末喜欢约球\n技术还在提升中,欢迎一起切磋\n平时在朝阳公园附近活动",
|
||||||
location: "北京朝阳",
|
city: "北京",
|
||||||
|
district: "朝阳",
|
||||||
occupation: "金融从业者",
|
occupation: "金融从业者",
|
||||||
ntrp_level: "NTRP 3.5",
|
ntrp_level: "NTRP 3.5",
|
||||||
is_following: false,
|
is_following: false,
|
||||||
@@ -68,22 +69,26 @@ const OtherUserPage: React.FC = () => {
|
|||||||
const { data: userData } = res;
|
const { data: userData } = res;
|
||||||
// setUserInfo({...res.data as UserInfo, avatar: data.avatar_url || require("@/static/userInfo/default_avatar.svg")});
|
// setUserInfo({...res.data as UserInfo, avatar: data.avatar_url || require("@/static/userInfo/default_avatar.svg")});
|
||||||
setUserInfo({
|
setUserInfo({
|
||||||
id: user_id || "",
|
id: parseInt(user_id || "") || 0,
|
||||||
nickname: userData.nickname || "",
|
nickname: userData.nickname || "",
|
||||||
avatar: userData.avatar_url || "",
|
avatar_url: userData.avatar_url || "",
|
||||||
join_date: userData.subscribe_time
|
join_date: userData.subscribe_time
|
||||||
? `${new Date(userData.subscribe_time).getFullYear()}年${new Date(userData.subscribe_time).getMonth() + 1
|
? `${new Date(userData.subscribe_time).getFullYear()}年${
|
||||||
|
new Date(userData.subscribe_time).getMonth() + 1
|
||||||
}月加入`
|
}月加入`
|
||||||
: "",
|
: "",
|
||||||
stats: {
|
stats: {
|
||||||
following: userData.stats?.following_count || 0,
|
following_count: userData.stats?.following_count || 0,
|
||||||
friends: userData.stats?.followers_count || 0,
|
followers_count: userData.stats?.followers_count || 0,
|
||||||
hosted: userData.stats?.hosted_games_count || 0,
|
hosted_games_count: userData.stats?.hosted_games_count || 0,
|
||||||
participated: userData.stats?.participated_games_count || 0,
|
participated_games_count:
|
||||||
|
userData.stats?.participated_games_count || 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
personal_profile: userData.personal_profile || "",
|
personal_profile: userData.personal_profile || "",
|
||||||
location: userData.city + userData.district || "",
|
province: userData.province || "",
|
||||||
|
city: userData.city || "",
|
||||||
|
district: userData.district || "",
|
||||||
occupation: userData.occupation || "",
|
occupation: userData.occupation || "",
|
||||||
ntrp_level: "",
|
ntrp_level: "",
|
||||||
phone: userData.phone || "",
|
phone: userData.phone || "",
|
||||||
@@ -132,7 +137,10 @@ const OtherUserPage: React.FC = () => {
|
|||||||
active_tab
|
active_tab
|
||||||
);
|
);
|
||||||
const sorted_games = games_data.sort((a, b) => {
|
const sorted_games = games_data.sort((a, b) => {
|
||||||
return new Date(a.original_start_time.replace(/\s/, 'T')).getTime() - new Date(b.original_start_time.replace(/\s/, 'T')).getTime();
|
return (
|
||||||
|
new Date(a.original_start_time.replace(/\s/, "T")).getTime() -
|
||||||
|
new Date(b.original_start_time.replace(/\s/, "T")).getTime()
|
||||||
|
);
|
||||||
});
|
});
|
||||||
const { notEndGames, finishedGames } = classifyGameRecords(sorted_games);
|
const { notEndGames, finishedGames } = classifyGameRecords(sorted_games);
|
||||||
setGameRecords(notEndGames);
|
setGameRecords(notEndGames);
|
||||||
@@ -157,7 +165,7 @@ const OtherUserPage: React.FC = () => {
|
|||||||
const handle_follow = async () => {
|
const handle_follow = async () => {
|
||||||
try {
|
try {
|
||||||
const new_follow_status = await UserService.toggle_follow(
|
const new_follow_status = await UserService.toggle_follow(
|
||||||
user_info.id,
|
user_info.id || "",
|
||||||
is_following
|
is_following
|
||||||
);
|
);
|
||||||
setIsFollowing(new_follow_status);
|
setIsFollowing(new_follow_status);
|
||||||
@@ -178,7 +186,9 @@ const OtherUserPage: React.FC = () => {
|
|||||||
// 处理发送消息
|
// 处理发送消息
|
||||||
const handle_send_message = () => {
|
const handle_send_message = () => {
|
||||||
Taro.navigateTo({
|
Taro.navigateTo({
|
||||||
url: `/mode_user/message/chat/index?user_id=${user_info.id}&nickname=${user_info.nickname}`,
|
url: `/mode_user/message/chat/index?user_id=${
|
||||||
|
user_info.id || ""
|
||||||
|
}&nickname=${user_info.nickname || ""}`,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user