695 lines
22 KiB
TypeScript
695 lines
22 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { View, Text, Image, ScrollView, Button } from "@tarojs/components";
|
|
import { PopupPicker } from "@/components/Picker/index";
|
|
import Taro from "@tarojs/taro";
|
|
import "./index.scss";
|
|
import { UserInfo } from "@/components/UserInfo";
|
|
import { UserService, PickerOption } from "@/services/userService";
|
|
import { clear_login_state } from "@/services/loginService";
|
|
import { convert_db_gender_to_display } from "@/utils/genderUtils";
|
|
import { EditModal } from "@/components";
|
|
import img from "@/config/images";
|
|
|
|
const EditProfilePage: React.FC = () => {
|
|
// 用户信息状态
|
|
const [user_info, setUserInfo] = useState<UserInfo>({
|
|
id: "1",
|
|
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: "",
|
|
});
|
|
|
|
// 表单状态
|
|
const [form_data, setFormData] = useState({
|
|
nickname: "",
|
|
personal_profile: "",
|
|
occupation: "",
|
|
ntrp_level: "4.0",
|
|
phone: "",
|
|
gender: "",
|
|
birthday: "2000-01-01",
|
|
country: "",
|
|
province: "",
|
|
city: "",
|
|
});
|
|
|
|
// 加载状态
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
// 编辑弹窗状态
|
|
const [edit_modal_visible, setEditModalVisible] = useState(false);
|
|
const [editing_field, setEditingField] = useState<string>("");
|
|
const [gender_picker_visible, setGenderPickerVisible] = useState(false);
|
|
const [birthday_picker_visible, setBirthdayPickerVisible] = useState(false);
|
|
const [location_picker_visible, setLocationPickerVisible] = useState(false);
|
|
const [ntrp_picker_visible, setNtrpPickerVisible] = useState(false);
|
|
const [occupation_picker_visible, setOccupationPickerVisible] =
|
|
useState(false);
|
|
|
|
// 职业数据
|
|
const [professions, setProfessions] = useState<PickerOption[]>([]);
|
|
|
|
// 城市数据
|
|
const [cities, setCities] = useState<PickerOption[]>([]);
|
|
|
|
// 页面加载时初始化数据
|
|
useEffect(() => {
|
|
load_user_info();
|
|
getProfessions();
|
|
getCities();
|
|
}, []);
|
|
|
|
const getProfessions = async () => {
|
|
try {
|
|
const res = await UserService.getProfessions();
|
|
setProfessions(res);
|
|
} catch (e) {
|
|
console.log("获取职业失败:", e);
|
|
}
|
|
};
|
|
const getCities = async () => {
|
|
try {
|
|
const res = await UserService.getCities();
|
|
setCities(res);
|
|
} catch (e) {
|
|
console.log("获取职业失败:", e);
|
|
}
|
|
};
|
|
|
|
// 加载用户信息
|
|
const load_user_info = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const user_data = await UserService.get_user_info();
|
|
setUserInfo(user_data);
|
|
setFormData({
|
|
nickname: user_data.nickname || "",
|
|
personal_profile: user_data.personal_profile || "",
|
|
occupation: user_data.occupation || "",
|
|
ntrp_level: user_data.ntrp_level || "NTRP 4.0",
|
|
phone: user_data.phone || "",
|
|
gender: user_data.gender || "",
|
|
birthday: user_data.birthday || "",
|
|
country: user_data.country || "",
|
|
province: user_data.province || "",
|
|
city: user_data.city || "",
|
|
});
|
|
} catch (error) {
|
|
console.error("加载用户信息失败:", error);
|
|
Taro.showToast({
|
|
title: "加载用户信息失败",
|
|
icon: "error",
|
|
duration: 2000,
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// 处理头像上传
|
|
const handle_avatar_upload = () => {
|
|
Taro.chooseImage({
|
|
count: 1,
|
|
sizeType: ["compressed"],
|
|
sourceType: ["album", "camera"],
|
|
success: async (res) => {
|
|
const tempFilePath = res.tempFilePaths[0];
|
|
try {
|
|
const avatar_url = await UserService.upload_avatar(tempFilePath);
|
|
setUserInfo((prev) => ({ ...prev, avatar: avatar_url }));
|
|
Taro.showToast({
|
|
title: "头像上传成功",
|
|
icon: "success",
|
|
});
|
|
} catch (error) {
|
|
console.error("头像上传失败:", error);
|
|
Taro.showToast({
|
|
title: "头像上传失败",
|
|
icon: "none",
|
|
});
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
// 处理编辑弹窗
|
|
const handle_open_edit_modal = (field: string) => {
|
|
if (field === "gender") {
|
|
setGenderPickerVisible(true);
|
|
return;
|
|
}
|
|
if (field === "birthday") {
|
|
setBirthdayPickerVisible(true);
|
|
return;
|
|
}
|
|
if (field === "location") {
|
|
setLocationPickerVisible(true);
|
|
return;
|
|
}
|
|
if (field === "ntrp_level") {
|
|
setNtrpPickerVisible(true);
|
|
return;
|
|
}
|
|
if (field === "occupation") {
|
|
setOccupationPickerVisible(true);
|
|
return;
|
|
}
|
|
if (field === "nickname") {
|
|
// 手动输入
|
|
setEditingField(field);
|
|
setEditModalVisible(true);
|
|
} else {
|
|
setEditingField(field);
|
|
setEditModalVisible(true);
|
|
}
|
|
};
|
|
|
|
const handle_edit_modal_save = async (value: string) => {
|
|
try {
|
|
// 调用更新用户信息接口,只传递修改的字段
|
|
const update_data = { [editing_field]: value };
|
|
await UserService.update_user_info(update_data);
|
|
|
|
// 更新本地状态
|
|
setFormData((prev) => ({ ...prev, [editing_field]: value }));
|
|
setUserInfo((prev) => ({ ...prev, [editing_field]: value }));
|
|
|
|
// 关闭弹窗
|
|
setEditModalVisible(false);
|
|
setEditingField("");
|
|
|
|
// 显示成功提示
|
|
Taro.showToast({
|
|
title: "保存成功",
|
|
icon: "success",
|
|
});
|
|
} catch (error) {
|
|
console.error("保存失败:", error);
|
|
Taro.showToast({
|
|
title: "保存失败",
|
|
icon: "error",
|
|
});
|
|
}
|
|
};
|
|
|
|
const handle_edit_modal_cancel = () => {
|
|
setEditModalVisible(false);
|
|
setEditingField("");
|
|
};
|
|
|
|
// 处理字段编辑
|
|
const handle_field_edit = async (
|
|
field: string | { [key: string]: string },
|
|
value?: string
|
|
) => {
|
|
try {
|
|
if (
|
|
typeof field === "object" &&
|
|
field !== null &&
|
|
!Array.isArray(field)
|
|
) {
|
|
await UserService.update_user_info({ ...field });
|
|
// 更新本地状态
|
|
setFormData((prev) => ({ ...prev, ...field }));
|
|
setUserInfo((prev) => ({ ...prev, ...field }));
|
|
} else {
|
|
// 调用更新用户信息接口,只传递修改的字段
|
|
const update_data = { [field as string]: value };
|
|
await UserService.update_user_info(update_data);
|
|
|
|
// 更新本地状态
|
|
setFormData((prev) => ({ ...prev, [field as string]: value }));
|
|
setUserInfo((prev) => ({ ...prev, [field as string]: value }));
|
|
}
|
|
|
|
// 显示成功提示
|
|
Taro.showToast({
|
|
title: "保存成功",
|
|
icon: "success",
|
|
});
|
|
} catch (error) {
|
|
console.error("保存失败:", error);
|
|
Taro.showToast({
|
|
title: "保存失败",
|
|
icon: "error",
|
|
});
|
|
}
|
|
};
|
|
|
|
// 处理性别选择
|
|
const handle_gender_change = (e: any) => {
|
|
const gender_value = e[0];
|
|
handle_field_edit("gender", gender_value);
|
|
};
|
|
|
|
// 处理生日选择
|
|
const handle_birthday_change = (e: any) => {
|
|
const [year, month, day] = e;
|
|
handle_field_edit(
|
|
"birthday",
|
|
`${year}-${String(month).padStart(2, "0")}-${String(day).padStart(
|
|
2,
|
|
"0"
|
|
)}`
|
|
);
|
|
};
|
|
|
|
// 处理地区选择
|
|
const handle_location_change = (e: any) => {
|
|
const [country, province, city] = e;
|
|
handle_field_edit({ country, province, city });
|
|
};
|
|
|
|
// 处理NTRP水平选择
|
|
const handle_ntrp_level_change = (e: any) => {
|
|
const ntrp_level_value = e[0];
|
|
handle_field_edit("ntrp_level", ntrp_level_value);
|
|
};
|
|
|
|
// 处理职业选择
|
|
const handle_occupation_change = (e: any) => {
|
|
const [country, province] = e;
|
|
handle_field_edit("occupation", `${country} ${province}`);
|
|
};
|
|
|
|
// 处理退出登录
|
|
const handle_logout = () => {
|
|
Taro.showModal({
|
|
title: "确认退出",
|
|
content: "确定要退出登录吗?",
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
// 清除用户数据
|
|
clear_login_state();
|
|
|
|
Taro.reLaunch({
|
|
url: "/login_pages/index/index",
|
|
});
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
const onGetPhoneNumber = async (e) => {
|
|
if (!e.detail || !e.detail.code) {
|
|
Taro.showToast({
|
|
title: "获取手机号失败,请重试",
|
|
icon: "none",
|
|
duration: 2000,
|
|
});
|
|
return;
|
|
}
|
|
try {
|
|
const phone = await UserService.parse_phone(e.detail.code);
|
|
handle_field_edit("phone", phone);
|
|
} catch (e) {
|
|
console.error("解析手机号失败:", e);
|
|
Taro.showToast({
|
|
title: "解析手机号失败,请重试",
|
|
icon: "none",
|
|
duration: 2000,
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<View className="edit_profile_page">
|
|
{/* 导航栏 */}
|
|
<View className="custom-navbar">
|
|
<View className="detail-navigator">
|
|
<View
|
|
className="detail-navigator-back"
|
|
onClick={() => {
|
|
Taro.navigateBack();
|
|
}}
|
|
>
|
|
<Image
|
|
className="detail-navigator-back-icon"
|
|
src={img.ICON_NAVIGATOR_BACK}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
{/* 主要内容 */}
|
|
<ScrollView className="main_content" scrollY>
|
|
{loading ? (
|
|
<View className="loading_container">
|
|
<Text className="loading_text">加载中...</Text>
|
|
</View>
|
|
) : (
|
|
<>
|
|
{/* 头像编辑区域 */}
|
|
<View className="avatar_section">
|
|
<View className="avatar_container" onClick={handle_avatar_upload}>
|
|
<Image className="avatar" src={user_info.avatar} />
|
|
<View className="avatar_overlay">
|
|
<Image
|
|
className="upload_icon"
|
|
src={require("@/static/userInfo/edit2.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 基本信息编辑 */}
|
|
<View className="form_section">
|
|
{/* 名字 */}
|
|
<View className="form_group">
|
|
<View
|
|
className="form_item"
|
|
onClick={() => handle_open_edit_modal("nickname")}
|
|
>
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/user.svg")}
|
|
/>
|
|
<Text className="item_label">名字</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
<Text className="item_value">
|
|
{form_data.nickname || "188的王晨"}
|
|
</Text>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
<View className="divider"></View>
|
|
</View>
|
|
|
|
{/* 性别 */}
|
|
<View className="form_group">
|
|
<View
|
|
className="form_item"
|
|
onClick={() => handle_open_edit_modal("gender")}
|
|
>
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/gender.svg")}
|
|
/>
|
|
<Text className="item_label">性别</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
<Text className="item_value">
|
|
{convert_db_gender_to_display(form_data.gender)}
|
|
</Text>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
<View className="divider"></View>
|
|
</View>
|
|
|
|
{/* 生日 */}
|
|
<View className="form_group">
|
|
<View
|
|
className="form_item"
|
|
onClick={() => handle_open_edit_modal("birthday")}
|
|
>
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/birthday.svg")}
|
|
/>
|
|
<Text className="item_label">生日</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
<Text className="item_value">{form_data.birthday}</Text>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 简介编辑 */}
|
|
<View className="form_section">
|
|
<View className="form_group">
|
|
<View
|
|
className="form_item"
|
|
onClick={() => handle_open_edit_modal("personal_profile")}
|
|
>
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/introduce.svg")}
|
|
/>
|
|
<Text className="item_label">简介</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
<Text className="item_value">
|
|
{form_data.personal_profile.replace(/\n/g, " ") ||
|
|
"介绍一下自己"}
|
|
</Text>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 地区、NTRP水平、职业 */}
|
|
<View className="form_section">
|
|
<View className="form_group">
|
|
{/* 地区 */}
|
|
<View
|
|
className="form_item"
|
|
onClick={() => handle_open_edit_modal("location")}
|
|
>
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/gender.svg")}
|
|
/>
|
|
<Text className="item_label">地区</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
<Text className="item_value">{`${form_data.country} ${form_data.province} ${form_data.city}`}</Text>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
<View className="divider"></View>
|
|
|
|
{/* NTRP水平 */}
|
|
<View
|
|
className="form_item"
|
|
onClick={() => handle_open_edit_modal("ntrp_level")}
|
|
>
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/ball.svg")}
|
|
/>
|
|
<Text className="item_label">NTRP 水平</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
<Text className="item_value">{form_data.ntrp_level}</Text>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
<View className="divider"></View>
|
|
|
|
{/* 职业 */}
|
|
<View
|
|
className="form_item"
|
|
onClick={() => handle_open_edit_modal("occupation")}
|
|
>
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/business.svg")}
|
|
/>
|
|
<Text className="item_label">职业</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
<Text className="item_value">{form_data.occupation}</Text>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 手机号 */}
|
|
<View className="form_section">
|
|
<View className="form_group">
|
|
<View className="form_item">
|
|
<View className="item_left">
|
|
<Image
|
|
className="item_icon"
|
|
src={require("@/static/userInfo/phone.svg")}
|
|
/>
|
|
<Text className="item_label">手机</Text>
|
|
</View>
|
|
<View className="item_right">
|
|
{/* <Input
|
|
className="item_input"
|
|
value={form_data.phone}
|
|
placeholder="请输入手机号"
|
|
type="number"
|
|
onInput={handle_phone_input}
|
|
onBlur={handle_phone_blur}
|
|
/> */}
|
|
<Button
|
|
className={form_data.phone ? "" : "placeholer"}
|
|
openType="getPhoneNumber"
|
|
onGetPhoneNumber={onGetPhoneNumber}
|
|
>
|
|
{form_data.phone || "未绑定"}
|
|
</Button>
|
|
<Image
|
|
className="arrow_icon"
|
|
src={require("@/static/list/icon-list-right-arrow.svg")}
|
|
/>
|
|
</View>
|
|
</View>
|
|
<View className="divider"></View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 注销账号 */}
|
|
<View className="logout_section">
|
|
<View className="logout_button" onClick={handle_logout}>
|
|
<Text className="logout_text">注销账号</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 退出登录 */}
|
|
<View className="logout_section">
|
|
<View className="logout_button" onClick={handle_logout}>
|
|
<Text className="logout_text">退出登录</Text>
|
|
</View>
|
|
</View>
|
|
</>
|
|
)}
|
|
</ScrollView>
|
|
|
|
{/* 编辑弹窗 */}
|
|
<EditModal
|
|
visible={edit_modal_visible}
|
|
type={editing_field}
|
|
title={editing_field === "nickname" ? "编辑名字" : "编辑简介"}
|
|
placeholder={
|
|
editing_field === "nickname"
|
|
? "请输入您的名字"
|
|
: "介绍一下你的喜好,或者训练习惯"
|
|
}
|
|
initialValue={form_data[editing_field as keyof typeof form_data] || ""}
|
|
maxLength={editing_field === "nickname" ? 20 : 100}
|
|
onSave={handle_edit_modal_save}
|
|
onCancel={handle_edit_modal_cancel}
|
|
validationMessage={
|
|
editing_field === "nickname"
|
|
? "请填写 1-20 个字符"
|
|
: "请填写 2-100 个字符"
|
|
}
|
|
/>
|
|
{/* 性别选择弹窗 */}
|
|
{gender_picker_visible && (
|
|
<PopupPicker
|
|
options={[
|
|
[
|
|
{ text: "男", value: "0" },
|
|
{ text: "女", value: "1" },
|
|
{ text: "保密", value: "2" },
|
|
],
|
|
]}
|
|
visible={gender_picker_visible}
|
|
setvisible={setGenderPickerVisible}
|
|
value={[form_data.gender]}
|
|
onChange={handle_gender_change}
|
|
/>
|
|
)}
|
|
{/* 生日选择弹窗 */}
|
|
{birthday_picker_visible && (
|
|
<PopupPicker
|
|
visible={birthday_picker_visible}
|
|
setvisible={setBirthdayPickerVisible}
|
|
value={[
|
|
new Date(form_data.birthday).getFullYear(),
|
|
new Date(form_data.birthday).getMonth() + 1,
|
|
new Date(form_data.birthday).getDate(),
|
|
]}
|
|
type="day"
|
|
onChange={handle_birthday_change}
|
|
/>
|
|
)}
|
|
{/* 地区选择弹窗 */}
|
|
{location_picker_visible && (
|
|
<PopupPicker
|
|
options={cities}
|
|
visible={location_picker_visible}
|
|
setvisible={setLocationPickerVisible}
|
|
value={[form_data.country, form_data.province, form_data.city]}
|
|
onChange={handle_location_change}
|
|
/>
|
|
)}
|
|
{/* NTRP水平选择弹窗 */}
|
|
{ntrp_picker_visible && (
|
|
<PopupPicker
|
|
options={[
|
|
[
|
|
{ text: "1.5", value: "1.5" },
|
|
{ text: "2.0", value: "2.0" },
|
|
{ text: "2.5", value: "2.5" },
|
|
{ text: "3.0", value: "3.0" },
|
|
{ text: "3.5", value: "3.5" },
|
|
{ text: "4.0", value: "4.0" },
|
|
{ text: "4.5", value: "4.5" },
|
|
],
|
|
]}
|
|
type="ntrp"
|
|
img={user_info.avatar}
|
|
visible={ntrp_picker_visible}
|
|
setvisible={setNtrpPickerVisible}
|
|
value={[form_data.ntrp_level]}
|
|
onChange={handle_ntrp_level_change}
|
|
/>
|
|
)}
|
|
{/* 职业选择弹窗 */}
|
|
{occupation_picker_visible && (
|
|
<PopupPicker
|
|
options={professions}
|
|
visible={occupation_picker_visible}
|
|
setvisible={setOccupationPickerVisible}
|
|
value={[...form_data.occupation.split(" ")]}
|
|
onChange={handle_occupation_change}
|
|
/>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default EditProfilePage;
|