用户生日、性别、职业、地址、ntrp水平 Picker编辑

This commit is contained in:
2025-09-17 23:25:32 +08:00
parent bb2b31af67
commit 7958e4ee3e
6 changed files with 208 additions and 142 deletions

View File

@@ -13,6 +13,26 @@ export const renderYearMonth = (minYear = 2020, maxYear = 2099) => {
]
}
export const renderYearMonthDay = (minYear = 2020, maxYear = 2099) => {
return [
// 年份列
Array.from({ length: maxYear - minYear + 1 }, (_, index) => ({
text: `${minYear + index}`,
value: minYear + index
})),
// 月份列
Array.from({ length: 12 }, (_, index) => ({
text: `${index + 1}`,
value: index + 1
})),
// 日期列 (默认31天具体天数需在onChange时动态调整)
Array.from({ length: 31 }, (_, index) => ({
text: `${index + 1}`,
value: index + 1
}))
]
}
export const renderHourMinute = (minHour = 0, maxHour = 23) => {
// 生成小时和分钟的选项数据
return [

View File

@@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react'
import CommonPopup from '@/components/CommonPopup'
import Picker from './Picker'
import { renderYearMonth, renderHourMinute } from './PickerData'
import { renderYearMonth, renderYearMonthDay, renderHourMinute } from './PickerData'
interface PickerOption {
text: string | number
value: string | number
@@ -12,55 +12,74 @@ interface PickerProps {
setvisible: (visible: boolean) => void
options?: PickerOption[][]
value?: (string | number)[]
type?: 'month' | 'hour' | null
type?: 'month' | 'day' | 'hour' | null
onConfirm?: (options: PickerOption[], values: (string | number)[]) => void
onChange?: ( value: (string | number)[] ) => void
onChange?: (value: (string | number)[]) => void
}
const PopupPicker = ({
visible,
setvisible,
value = [],
onConfirm,
const PopupPicker = ({
visible,
setvisible,
value = [],
onConfirm,
onChange,
options = [],
type = null
}: PickerProps) => {
const [defaultValue, setDefaultValue] = useState<(string | number)[]>([])
const [defaultOptions, setDefaultOptions] = useState<PickerOption[][]>([])
const changePicker = (options: any[], values: any, columnIndex: number) => {
debugger
if (onChange) {
console.log('picker onChange', columnIndex, values, options)
console.log('picker onChange', columnIndex, values, options);
setDefaultValue(values)
if (type === 'day' && JSON.stringify(defaultValue) !== JSON.stringify(values)) {
const [year, month] = values;
const daysInMonth = new Date(Number(year), Number(month), 0).getDate();
const dayOptions = Array.from({ length: daysInMonth }, (_, i) => ({
text: i + 1 + '日',
value: i + 1,
}));
const newOptions = [...defaultOptions];
if (JSON.stringify(newOptions[2]) !== JSON.stringify(dayOptions)) {
newOptions[2] = dayOptions;
setDefaultOptions(newOptions);
}
}
if (JSON.stringify(defaultValue) !== JSON.stringify(values)) {
setDefaultValue(values);
}
}
}
const handleConfirm = () => {
console.log(defaultValue,'defaultValue');
console.log(defaultValue, 'defaultValue');
onChange(defaultValue)
setvisible(false)
}
const dialogClose = () => {
setvisible(false)
}
useEffect(() => {
if (type === 'month') {
setDefaultOptions(renderYearMonth())
} else if (type === 'day') {
setDefaultOptions(renderYearMonthDay())
} else if (type === 'hour') {
setDefaultOptions(renderHourMinute())
} else {
setDefaultOptions(options)
}
}, [type])
// useEffect(() => {
// if (value.length > 0 && defaultOptions.length > 0) {
// setDefaultValue([...value])
// }
// }, [value, defaultOptions])
// useEffect(() => {
// if (value.length > 0 && defaultOptions.length > 0) {
// setDefaultValue([...value])
// }
// }, [value, defaultOptions])
return (
<>
<CommonPopup

View File

@@ -19,7 +19,6 @@ export interface UserInfo {
participated: number;
};
personal_profile: string;
location: string;
occupation: string;
ntrp_level: string;
phone?: string;
@@ -31,6 +30,9 @@ export interface UserInfo {
is_following?: boolean;
tags?: string[];
ongoing_games?: string[];
country?: string;
province?: string;
city?: string;
}
// 用户信息卡片组件属性

View File

@@ -31,6 +31,7 @@ interface UserDetailData {
personal_profile: string;
occupation: string;
birthday: string;
ntrp_level: string,
stats: {
followers_count: number;
following_count: number;
@@ -161,6 +162,7 @@ export class UserService {
id: game.id,
title: game.title || '未命名球局',
start_time: date_time,
original_start_time: game.start_time,
end_time: game.end_time || '',
location: location,
distance_km: parseFloat(distance.replace('km', '')) || 0,
@@ -247,12 +249,14 @@ export class UserService {
},
personal_profile: userData.personal_profile || '',
location: userData.city + userData.district || '',
occupation: userData.occupation || '',
ntrp_level: '',
ntrp_level: userData.ntrp_level || '',
phone: userData.phone || '',
gender: userData.gender || '',
birthday: userData.birthday || '',
country: userData.country || '',
province: userData.province || '',
city: userData.city || '',
};
} else {
throw new Error(response.message || '获取用户信息失败');
@@ -281,7 +285,6 @@ export class UserService {
}
}
});
// 如果没有需要更新的字段,直接返回
if (Object.keys(filtered_data).length === 0) {
console.log('没有需要更新的字段');

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { View, Text, Image, ScrollView, Picker, Input, Button } from '@tarojs/components';
import { PopupPicker } from '@/components/Picker/index'
import Taro from '@tarojs/taro';
import './index.scss';
import { UserInfo } from '@/components/UserInfo';
@@ -23,23 +24,27 @@ const EditProfilePage: React.FC = () => {
participated: 0
},
personal_profile: '加载中...',
location: '加载中...',
occupation: '加载中...',
ntrp_level: 'NTRP 3.0',
phone: '',
gender: ''
gender: '',
country: '',
province: '',
city: '',
});
// 表单状态
const [form_data, setFormData] = useState({
nickname: '',
personal_profile: '',
location: '',
occupation: '',
ntrp_level: '4.0',
phone: '',
gender: '',
birthday: '2000-01-01'
birthday: '2000-01-01',
country: '',
province: '',
city: ''
});
// 加载状态
@@ -48,6 +53,11 @@ const EditProfilePage: React.FC = () => {
// 编辑弹窗状态
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);
// 页面加载时初始化数据
useEffect(() => {
@@ -63,12 +73,14 @@ const EditProfilePage: React.FC = () => {
setFormData({
nickname: user_data.nickname || '',
personal_profile: user_data.personal_profile || '',
location: user_data.location || '',
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 || '', // 默认生日,实际应该从用户数据获取
birthday: user_data.birthday || '',
country: user_data.country || '',
province: user_data.province || '',
city: user_data.city || ''
});
} catch (error) {
console.error('加载用户信息失败:', error);
@@ -110,6 +122,26 @@ const EditProfilePage: React.FC = () => {
// 处理编辑弹窗
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);
@@ -154,15 +186,22 @@ const EditProfilePage: React.FC = () => {
};
// 处理字段编辑
const handle_field_edit = async (field: string, value: string) => {
const handle_field_edit = async (field: string | { [key: string]: string }, value?: string) => {
try {
// 调用更新用户信息接口,只传递修改的字段
const update_data = { [field]: value };
await UserService.update_user_info(update_data);
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]: value }));
setUserInfo(prev => ({ ...prev, [field]: value }));
// 更新本地状态
setFormData(prev => ({ ...prev, [field as string]: value }));
setUserInfo(prev => ({ ...prev, [field as string]: value }));
}
// 显示成功提示
Taro.showToast({
@@ -180,64 +219,35 @@ const EditProfilePage: React.FC = () => {
// 处理性别选择
const handle_gender_change = (e: any) => {
const gender_value = e.detail.value;
const gender_value = e[0];
handle_field_edit('gender', gender_value);
};
// 处理生日选择
const handle_birthday_change = (e: any) => {
const birthday_value = e.detail.value;
handle_field_edit('birthday', birthday_value);
const [year, month, day] = e;
handle_field_edit('birthday', `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`);
};
// NTRP水平输入 - 实时更新本地状态
const handle_ntrp_level_input = (e: any) => {
const ntrp_level_value = e.detail.value;
setFormData(prev => ({ ...prev, ntrp_level: ntrp_level_value }));
}
// NTRP水平输入 - 失去焦点时保存到服务器
const handle_ntrp_level_blur = (e: any) => {
const ntrp_level_value = e.detail.value;
// 处理地区选择
const handle_location_change = (e: any) => {
debugger
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_input = (e: any) => {
const occupation_value = e.detail.value;
setFormData(prev => ({ ...prev, occupation: occupation_value }));
};
// 处理职业输入 - 失去焦点时保存到服务器
const handle_occupation_blur = (e: any) => {
const occupation_value = e.detail.value;
handle_field_edit('occupation', occupation_value);
// 处理职业选择
const handle_occupation_change = (e: any) => {
const [country, province] = e;
handle_field_edit('occupation', `${country} ${province}`);
};
// 处理地区输入 - 实时更新本地状态
const handle_location_input = (e: any) => {
const location_value = e.detail.value;
setFormData(prev => ({ ...prev, location: location_value }));
};
// 处理地区输入 - 失去焦点时保存到服务器
const handle_location_blur = (e: any) => {
const location_value = e.detail.value;
handle_field_edit('location', location_value);
};
// // 处理手机号输入 - 实时更新本地状态
// const handle_phone_input = (e: any) => {
// const phone_value = e.detail.value;
// setFormData(prev => ({ ...prev, phone: phone_value }));
// };
// // 处理手机号输入 - 失去焦点时保存到服务器
// const handle_phone_blur = (e: any) => {
// const phone_value = e.detail.value;
// handle_field_edit('phone', phone_value);
// };
// 处理退出登录
const handle_logout = () => {
Taro.showModal({
@@ -333,46 +343,31 @@ const EditProfilePage: React.FC = () => {
{/* 性别 */}
<View className="form_group">
<Picker
mode="selector"
range={['男', '女']}
value={form_data.gender === '0' ? 0 : form_data.gender === '1' ? 1 : 0}
onChange={handle_gender_change}
>
<View className="form_item">
<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 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>
</Picker>
<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">
<Picker
mode="date"
value={form_data.birthday}
onChange={handle_birthday_change}
>
<View className="form_item">
<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 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>
</Picker>
<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>
@@ -398,58 +393,39 @@ const EditProfilePage: React.FC = () => {
<View className="form_section">
<View className="form_group">
{/* 地区 */}
<View className="form_item">
<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">
<Input
className="item_input"
value={form_data.location}
placeholder="请输入地区"
onInput={handle_location_input}
onBlur={handle_location_blur}
/>
<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">
<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> */}
<Input
className="item_input"
value={form_data.ntrp_level}
placeholder="请输入NTRP水平"
onInput={handle_ntrp_level_input}
onBlur={handle_ntrp_level_blur}
/>
<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">
<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">
<Input
className="item_input"
value={form_data.occupation}
placeholder="请输入职业"
onInput={handle_occupation_input}
onBlur={handle_occupation_blur}
/>
<Text className="item_value">{form_data.occupation}</Text>
<Image className="arrow_icon" src={require('@/static/list/icon-list-right-arrow.svg')} />
</View>
</View>
@@ -510,6 +486,53 @@ const EditProfilePage: React.FC = () => {
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={[[{ text: "中国", value: "中国" }], [{ text: "上海", value: "上海" }], [{ text: "浦东新区", value: "浦东新区" }, {text: "静安区", value: "静安区"}]]}
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' },
]]}
visible={ntrp_picker_visible}
setvisible={setNtrpPickerVisible}
value={[form_data.ntrp_level]}
onChange={handle_ntrp_level_change} />}
{/* 职业选择弹窗 */}
{occupation_picker_visible && <PopupPicker
options={[[{ text: "时尚", value: "时尚" }], [{ text: "美妆博主", value: "美妆博主" },{ text: "设计师", value: "设计师" }]]}
visible={occupation_picker_visible}
setvisible={setOccupationPickerVisible}
value={[...form_data.occupation.split(' ')]}
onChange={handle_occupation_change} />}
</View>
);
};

View File

@@ -133,9 +133,8 @@ const OtherUserPage: React.FC = () => {
active_tab
);
const sorted_games = games_data.sort((a, b) => {
return new Date(b.start_time.replace(/\s/, 'T')).getTime() - new Date(a.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();
});
console.log('xxxxxxxxxxxxxxxxxx', sorted_games)
const { notEndGames, finishedGames } = classifyGameRecords(sorted_games);
setGameRecords(notEndGames);
setEndedGameRecords(finishedGames);