This commit is contained in:
张成
2025-09-07 20:12:54 +08:00
parent 45f85a8572
commit fa0dc5bbd7
5 changed files with 217 additions and 75 deletions

View File

@@ -82,10 +82,11 @@
// 内容区域 // 内容区域
.modal_content { .modal_content {
padding: 0px 16px 20px; padding: 16px 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
box-sizing: border-box;
.input_container { .input_container {
display: flex; display: flex;
@@ -96,7 +97,13 @@
border-radius: 12px; border-radius: 12px;
padding: 10px 16px; padding: 10px 16px;
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06); box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.06);
min-height: 120px;
// 名字输入时的容器样式
&:has(.nickname_input) {
min-height: 40px;
padding: 10px 16px;
}
.text_input { .text_input {
flex: 1; flex: 1;
@@ -109,11 +116,21 @@
background: transparent; background: transparent;
outline: none; outline: none;
resize: none; resize: none;
min-height: 80px;
min-height: 120px;
&::placeholder { &::placeholder {
color: rgba(60, 60, 67, 0.3); color: rgba(60, 60, 67, 0.3);
} }
// 名字输入特殊样式
&.nickname_input {
min-height: 80px;
min-height: 20px;
height: 20px;
line-height: 20px;
padding: 0;
}
} }
.char_count { .char_count {

View File

@@ -1,11 +1,12 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { View, Text, Textarea, Button } from '@tarojs/components'; import { View, Text, Textarea, Input, Picker } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import './EditModal.scss'; import './EditModal.scss';
interface EditModalProps { interface EditModalProps {
visible: boolean; visible: boolean;
title: string; title: string;
type: string;
placeholder: string; placeholder: string;
initialValue: string; initialValue: string;
maxLength: number; maxLength: number;
@@ -17,6 +18,7 @@ interface EditModalProps {
const EditModal: React.FC<EditModalProps> = ({ const EditModal: React.FC<EditModalProps> = ({
visible, visible,
title, title,
type,
placeholder, placeholder,
initialValue, initialValue,
maxLength, maxLength,
@@ -82,6 +84,21 @@ const EditModal: React.FC<EditModalProps> = ({
<View className="modal_content"> <View className="modal_content">
{/* 文本输入区域 */} {/* 文本输入区域 */}
<View className="input_container"> <View className="input_container">
{type === 'nickname' ? (
<Input
className="text_input nickname_input"
value={value}
type="nickname"
placeholder={placeholder}
maxlength={maxLength}
onInput={handle_input_change}
adjustPosition={true}
confirmType="done"
autoFocus={true}
/>
) : (
<>
<Textarea <Textarea
className="text_input" className="text_input"
value={value} value={value}
@@ -93,6 +110,8 @@ const EditModal: React.FC<EditModalProps> = ({
<View className="char_count"> <View className="char_count">
<Text className="count_text">{value.length}/{maxLength}</Text> <Text className="count_text">{value.length}/{maxLength}</Text>
</View> </View>
</>
)}
</View> </View>
{/* 验证提示 */} {/* 验证提示 */}

View File

@@ -15,7 +15,7 @@ export interface UserInfo {
hosted: number; hosted: number;
participated: number; participated: number;
}; };
bio: string; personal_profile: string;
location: string; location: string;
occupation: string; occupation: string;
ntrp_level: string; ntrp_level: string;
@@ -155,7 +155,7 @@ export const UserInfoCard: React.FC<UserInfoCardProps> = ({
<Text className="tag_text">{user_info.location || '未设置'}</Text> <Text className="tag_text">{user_info.location || '未设置'}</Text>
</View> </View>
</View> </View>
<Text className="bio_text">{user_info.bio}</Text> <Text className="bio_text">{user_info.personal_profile}</Text>
</View> </View>
</View> </View>
); );

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { View, Text, Image, ScrollView, Input } from '@tarojs/components'; import { View, Text, Image, ScrollView, Picker, Input } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import './index.scss'; import './index.scss';
import { UserInfo } from '@/components/UserInfo'; import { UserInfo } from '@/components/UserInfo';
@@ -19,8 +19,7 @@ const EditProfilePage: React.FC = () => {
hosted: 0, hosted: 0,
participated: 0 participated: 0
}, },
tags: ['加载中...'], personal_profile: '加载中...',
bio: '加载中...',
location: '加载中...', location: '加载中...',
occupation: '加载中...', occupation: '加载中...',
ntrp_level: 'NTRP 3.0', ntrp_level: 'NTRP 3.0',
@@ -31,7 +30,7 @@ const EditProfilePage: React.FC = () => {
// 表单状态 // 表单状态
const [form_data, setFormData] = useState({ const [form_data, setFormData] = useState({
nickname: '', nickname: '',
bio: '', personal_profile: '',
location: '', location: '',
occupation: '', occupation: '',
ntrp_level: '4.0', ntrp_level: '4.0',
@@ -59,11 +58,11 @@ const EditProfilePage: React.FC = () => {
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 || '',
bio: user_data.bio, personal_profile: user_data.personal_profile || '',
location: user_data.location, location: user_data.location || '',
occupation: user_data.occupation, occupation: user_data.occupation || '',
ntrp_level: user_data.ntrp_level.replace('NTRP ', ''), 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: '2000-01-01' // 默认生日,实际应该从用户数据获取 birthday: '2000-01-01' // 默认生日,实际应该从用户数据获取
@@ -80,17 +79,6 @@ const EditProfilePage: React.FC = () => {
} }
}; };
// 处理输入变化
const handle_input_change = (field: string, value: string) => {
setFormData(prev => ({
...prev,
[field]: value
}));
};
// 处理头像上传 // 处理头像上传
const handle_avatar_upload = () => { const handle_avatar_upload = () => {
Taro.chooseImage({ Taro.chooseImage({
@@ -119,14 +107,42 @@ const EditProfilePage: React.FC = () => {
// 处理编辑弹窗 // 处理编辑弹窗
const handle_open_edit_modal = (field: string) => { const handle_open_edit_modal = (field: string) => {
if (field === 'nickname') {
// 手动输入
setEditingField(field); setEditingField(field);
setEditModalVisible(true); setEditModalVisible(true);
} else {
setEditingField(field);
setEditModalVisible(true);
}
}; };
const handle_edit_modal_save = (value: string) => { 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 })); setFormData(prev => ({ ...prev, [editing_field]: value }));
setUserInfo(prev => ({ ...prev, [editing_field]: value }));
// 关闭弹窗
setEditModalVisible(false); setEditModalVisible(false);
setEditingField(''); setEditingField('');
// 显示成功提示
Taro.showToast({
title: '保存成功',
icon: 'success'
});
} catch (error) {
console.error('保存失败:', error);
Taro.showToast({
title: '保存失败',
icon: 'error'
});
}
}; };
const handle_edit_modal_cancel = () => { const handle_edit_modal_cancel = () => {
@@ -134,6 +150,58 @@ const EditProfilePage: React.FC = () => {
setEditingField(''); setEditingField('');
}; };
// 处理字段编辑
const handle_field_edit = async (field: string, value: string) => {
try {
// 调用更新用户信息接口,只传递修改的字段
const update_data = { [field]: value };
await UserService.update_user_info(update_data);
// 更新本地状态
setFormData(prev => ({ ...prev, [field]: value }));
setUserInfo(prev => ({ ...prev, [field]: 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.detail.value;
const gender_text = gender_value === 'male' ? '男' : '女';
handle_field_edit('gender', gender_text);
};
// 处理生日选择
const handle_birthday_change = (e: any) => {
const birthday_value = e.detail.value;
handle_field_edit('birthday', birthday_value);
};
// 处理职业输入
const handle_occupation_change = (e: any) => {
const occupation_value = e.detail.value;
handle_field_edit('occupation', occupation_value);
};
// 处理地区输入
const handle_location_change = (e: any) => {
const location_value = e.detail.value;
handle_field_edit('location', location_value);
};
// 处理退出登录 // 处理退出登录
const handle_logout = () => { const handle_logout = () => {
Taro.showModal({ Taro.showModal({
@@ -183,18 +251,13 @@ const EditProfilePage: React.FC = () => {
<View className="form_section"> <View className="form_section">
{/* 名字 */} {/* 名字 */}
<View className="form_group"> <View className="form_group">
<View className="form_item"> <View className="form_item" onClick={() => handle_open_edit_modal('nickname')}>
<View className="item_left"> <View className="item_left">
<Image className="item_icon" src={require('../../../static/userInfo/user1.svg')} /> <Image className="item_icon" src={require('../../../static/userInfo/user1.svg')} />
<Text className="item_label"></Text> <Text className="item_label"></Text>
</View> </View>
<View className="item_right"> <View className="item_right">
<Input <Text className="item_value">{form_data.nickname || '188的王晨'}</Text>
className="item_input"
value={form_data.nickname}
placeholder="188的王晨"
onInput={(e) => handle_input_change('nickname', e.detail.value)}
/>
<Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} /> <Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} />
</View> </View>
</View> </View>
@@ -203,21 +266,33 @@ const EditProfilePage: React.FC = () => {
{/* 性别 */} {/* 性别 */}
<View className="form_group"> <View className="form_group">
<Picker
mode="selector"
range={['男', '女']}
value={form_data.gender === '男' ? 0 : 1}
onChange={handle_gender_change}
>
<View className="form_item"> <View className="form_item">
<View className="item_left"> <View className="item_left">
<Image className="item_icon" src={require('../../../static/userInfo/user2.svg')} /> <Image className="item_icon" src={require('../../../static/userInfo/user2.svg')} />
<Text className="item_label"></Text> <Text className="item_label"></Text>
</View> </View>
<View className="item_right"> <View className="item_right">
<Text className="item_value">{form_data.gender || ''}</Text> <Text className="item_value">{form_data.gender || '请选择'}</Text>
<Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} /> <Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} />
</View> </View>
</View> </View>
</Picker>
<View className="divider"></View> <View className="divider"></View>
</View> </View>
{/* 生日 */} {/* 生日 */}
<View className="form_group"> <View className="form_group">
<Picker
mode="date"
value={form_data.birthday}
onChange={handle_birthday_change}
>
<View className="form_item"> <View className="form_item">
<View className="item_left"> <View className="item_left">
<Image className="item_icon" src={require('../../../static/userInfo/tennis.svg')} /> <Image className="item_icon" src={require('../../../static/userInfo/tennis.svg')} />
@@ -228,20 +303,21 @@ const EditProfilePage: React.FC = () => {
<Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} /> <Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} />
</View> </View>
</View> </View>
</Picker>
</View> </View>
</View> </View>
{/* 简介编辑 */} {/* 简介编辑 */}
<View className="form_section"> <View className="form_section">
<View className="form_group"> <View className="form_group">
<View className="form_item" onClick={() => handle_open_edit_modal('bio')}> <View className="form_item" onClick={() => handle_open_edit_modal('personal_profile')}>
<View className="item_left"> <View className="item_left">
<Image className="item_icon" src={require('../../../static/userInfo/message.svg')} /> <Image className="item_icon" src={require('../../../static/userInfo/message.svg')} />
<Text className="item_label"></Text> <Text className="item_label"></Text>
</View> </View>
<View className="item_right"> <View className="item_right">
<Text className="item_value"> <Text className="item_value">
{form_data.bio || '介绍一下自己'} {form_data.personal_profile || '介绍一下自己'}
</Text> </Text>
<Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} /> <Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} />
</View> </View>
@@ -259,8 +335,12 @@ const EditProfilePage: React.FC = () => {
<Text className="item_label"></Text> <Text className="item_label"></Text>
</View> </View>
<View className="item_right"> <View className="item_right">
<Text className="item_value">{form_data.location || '上海 黄浦'}</Text> <Input
<Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} /> className="item_input"
value={form_data.location}
placeholder="请输入地区"
onBlur={(e) => handle_location_change(e)}
/>
</View> </View>
</View> </View>
<View className="divider"></View> <View className="divider"></View>
@@ -285,8 +365,12 @@ const EditProfilePage: React.FC = () => {
<Text className="item_label"></Text> <Text className="item_label"></Text>
</View> </View>
<View className="item_right"> <View className="item_right">
<Text className="item_value">{form_data.occupation || '互联网'}</Text> <Input
<Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} /> className="item_input"
value={form_data.occupation}
placeholder="请输入职业"
onBlur={(e) => handle_occupation_change(e)}
/>
</View> </View>
</View> </View>
</View> </View>
@@ -301,8 +385,13 @@ const EditProfilePage: React.FC = () => {
<Text className="item_label"></Text> <Text className="item_label"></Text>
</View> </View>
<View className="item_right"> <View className="item_right">
<Text className="item_value">{form_data.phone || '+86 130 1234 1234'}</Text> <Input
<Image className="arrow_icon" src={require('../../../static/list/icon-list-right-arrow.svg')} /> className="item_input"
value={form_data.phone}
placeholder="请输入手机号"
type="number"
onBlur={(e) => handle_field_edit('phone', e.detail.value)}
/>
</View> </View>
</View> </View>
<View className="divider"></View> <View className="divider"></View>
@@ -322,13 +411,14 @@ const EditProfilePage: React.FC = () => {
{/* 编辑弹窗 */} {/* 编辑弹窗 */}
<EditModal <EditModal
visible={edit_modal_visible} visible={edit_modal_visible}
title="编辑简介" type={editing_field}
placeholder="介绍一下你的喜好,或者训练习惯" title={editing_field === 'nickname' ? '编辑名字' : '编辑简介'}
placeholder={editing_field === 'nickname' ? '请输入您的名字' : '介绍一下你的喜好,或者训练习惯'}
initialValue={form_data[editing_field as keyof typeof form_data] || ''} initialValue={form_data[editing_field as keyof typeof form_data] || ''}
maxLength={100} maxLength={editing_field === 'nickname' ? 20 : 100}
onSave={handle_edit_modal_save} onSave={handle_edit_modal_save}
onCancel={handle_edit_modal_cancel} onCancel={handle_edit_modal_cancel}
validationMessage="请填写 2-100 个字符" validationMessage={editing_field === 'nickname' ? '请填写 1-20 个字符' : '请填写 2-100 个字符'}
/> />
</View> </View>
); );

View File

@@ -265,7 +265,7 @@ export class UserService {
participated: userData.stats?.participated_games_count || 0 participated: userData.stats?.participated_games_count || 0
}, },
bio: '', personal_profile: '',
location:userData.province + userData.city || '', location:userData.province + userData.city || '',
occupation: '', occupation: '',
ntrp_level: '', ntrp_level: '',
@@ -282,6 +282,22 @@ export class UserService {
} }
} }
// 更新用户信息
static async update_user_info(update_data: Partial<UserInfo>): Promise<void> {
try {
const response = await httpService.post(API_CONFIG.USER.UPDATE, update_data, {
showLoading: true
});
if (response.code !== 0) {
throw new Error(response.message || '更新用户信息失败');
}
} catch (error) {
console.error('更新用户信息失败:', error);
throw error;
}
}
// 获取用户主办的球局 // 获取用户主办的球局
static async get_hosted_games(user_id: string): Promise<any[]> { static async get_hosted_games(user_id: string): Promise<any[]> {
try { try {