From 8a3e41cef6fc9852336eea67e9fb767c792c52d1 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Sun, 28 Sep 2025 17:30:44 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=B4=A6=E5=8D=95=E3=80=81?= =?UTF-8?q?=E5=9F=8E=E5=B8=82=E5=92=8C=E8=81=8C=E4=B8=9A=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Picker/PopupPicker.tsx | 2 +- src/config/api.ts | 4 +- src/services/userService.ts | 63 +++- src/user_pages/downloadBill/index.tsx | 131 ++++--- src/user_pages/edit/index.tsx | 515 +++++++++++++++++--------- 5 files changed, 486 insertions(+), 229 deletions(-) diff --git a/src/components/Picker/PopupPicker.tsx b/src/components/Picker/PopupPicker.tsx index c1c58cb..75de996 100644 --- a/src/components/Picker/PopupPicker.tsx +++ b/src/components/Picker/PopupPicker.tsx @@ -17,7 +17,7 @@ interface PickerOption { interface PickerProps { visible: boolean; setvisible: (visible: boolean) => void; - options?: PickerOption[][]; + options?: PickerOption[][] | PickerOption[]; value?: (string | number)[]; type?: "month" | "day" | "hour" | "ntrp" | null; img?: string; diff --git a/src/config/api.ts b/src/config/api.ts index f54531e..489dd2d 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -29,7 +29,9 @@ export const API_CONFIG = { CREATE: '/game/create', JOIN: '/game/join', LEAVE: '/game/leave' - } + }, + PROFESSIONS: '/professions/tree', + CITIS: '/admin/wch_cities/page' }; // 请求拦截器配置 diff --git a/src/services/userService.ts b/src/services/userService.ts index 02fe672..3578eeb 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -40,6 +40,17 @@ interface UserDetailData { }; } +export interface PickerOption { + text: string | number; + value: string | number; + children?: PickerOption[]; +} + +export interface Profession { + name: string; + children: Profession[] | []; +} + // 用户详细信息接口(从 loginService 移过来) export interface UserInfoType { id: number @@ -119,6 +130,21 @@ interface BackendGameData { }[]; } +const formatOptions = (data: Profession[]): PickerOption[] => { + return data.map((item: Profession) => { + const { name: text, children } = item; + const itm: PickerOption = { + text, + value: text, + children: children ? formatOptions(children) : [] + } + if (!itm.children!.length) { + delete itm.children + } + return itm + }) +} + // 用户服务类 export class UserService { // 数据转换函数:将后端数据转换为ListContainer期望的格式 @@ -206,7 +232,7 @@ export class UserService { date_str = `明天(${weekday})`; } else if (start_date.getTime() === day_after_tomorrow.getTime()) { date_str = `后天(${weekday})`; - } else if(this.is_date_in_this_week(start_time)) { + } else if (this.is_date_in_this_week(start_time)) { date_str = weekday; } else { date_str = `${start_time.getFullYear()}-${(start_time.getMonth() + 1).toString().padStart(2, '0')}-${start_time.getDate().toString().padStart(2, '0')}(${weekday})`; @@ -238,7 +264,7 @@ export class UserService { if (response.code === 0) { const userData = response.data; return { - id: userData.id || '', + id: userData.id || '', nickname: userData.nickname || '', avatar: userData.avatar_url || '', join_date: userData.subscribe_time ? `${new Date(userData.subscribe_time).getFullYear()}年${new Date(userData.subscribe_time).getMonth() + 1}月加入` : '', @@ -492,6 +518,39 @@ export class UserService { return ''; } } + + // 获取职业树 + static async getProfessions(): Promise<[] | PickerOption[]> { + try { + const response = await httpService.post(API_CONFIG.PROFESSIONS); + const { code, data, message } = response; + if (code === 0) { + return formatOptions(data || []); + } else { + throw new Error(message || '获取职业树失败'); + } + } catch (error) { + console.error('获取职业树失败:', error); + return []; + } + } + + // 获取城市树 + static async getCities(): Promise<[] | PickerOption[]> { + try { + const response = await httpService.post(API_CONFIG.CITIS); + const { code, data, message } = response; + if (code === 0) { + return formatOptions(data || []); + } else { + throw new Error(message || '获取城市树失败'); + } + } catch (error) { + console.error('获取职业树失败:', error); + return []; + } + } + } // 从 loginService 移过来的用户相关方法 diff --git a/src/user_pages/downloadBill/index.tsx b/src/user_pages/downloadBill/index.tsx index f31a56a..ef248b5 100644 --- a/src/user_pages/downloadBill/index.tsx +++ b/src/user_pages/downloadBill/index.tsx @@ -7,6 +7,7 @@ import "./index.scss"; import { DialogCalendarCard } from "@/components/index"; // import { CalendarUI } from "@/components"; import { CommonPopup } from "@/components"; +import httpService from "@/services/httpService"; export enum TransactionSubType { All = "", @@ -25,15 +26,13 @@ interface Option { value: T; } interface TransactionLoadParams { - page: number; - limit: number; - type: TransactionType; transaction_sub_type: TransactionSubType; - keyword?: string; - date?: string; + date_range?: string[]; } const DownloadBill: React.FC = () => { const [dateRange, setDateRange] = useState({ start: "", end: "" }); + const [transactionSubType, setTransactionSubType] = + useState(TransactionSubType.All); const [dateType, setDateType] = useState("week"); const [visible, setVisible] = useState(false); const [show_download_popup, set_show_download_popup] = useState(false); @@ -77,10 +76,12 @@ const DownloadBill: React.FC = () => { } switch (range) { case "week": + setCurrentTimeValue(new Date()); setDateType("week"); culculateDateRange("week"); break; case "month": + setCurrentTimeValue(new Date()); setDateType("month"); culculateDateRange("month"); break; @@ -103,7 +104,8 @@ const DownloadBill: React.FC = () => { }; const handlePasswordInput = (e: any) => { const value = e.detail.value; - const [one = "", two = "", three = "", four = "", five = "", six = ""] = value.split(""); + const [one = "", two = "", three = "", four = "", five = "", six = ""] = + value.split(""); setPassword([one, two, three, four, five, six]); if (value.length === 6) { // const timer = setTimeout(() => { @@ -132,7 +134,7 @@ const DownloadBill: React.FC = () => { // } // }, 100); } - } + }; const transaction_type_options: Option[] = [ { label: "全部", @@ -157,13 +159,30 @@ const DownloadBill: React.FC = () => { ]; const [load_transactions_params, set_load_transactions_params] = useState({ - page: 1, - limit: 20, - type: TransactionType.All, transaction_sub_type: TransactionSubType.All, - keyword: "", - date: "", + date_range: [], }); + + const handleClose = () => { + setTransactionSubType(load_transactions_params.transaction_sub_type); + setShowFilterPopup(false); + }; + const handleTypeConfirm = () => { + set_load_transactions_params((prev) => { + return { ...prev, transaction_sub_type: transactionSubType }; + }); + setShowFilterPopup(false); + }; + const handleDownloadBill = async () => { + try { + const { transaction_sub_type } = load_transactions_params; + const { start, end } = dateRange; + const date_range = [start, end]; + await httpService.post("/wallet/download_bill", {transaction_sub_type, date_range}); + } catch (error) { + console.error(error); + } + }; return ( @@ -177,10 +196,22 @@ const DownloadBill: React.FC = () => { 小程序消息 */} - { setShowFilterPopup(true) }}> + { + setShowFilterPopup(true); + }} + > 交易类型 - 全部 + + { + transaction_type_options.find( + (item) => + item.value === load_transactions_params.transaction_sub_type + )?.label + } + @@ -195,8 +226,9 @@ const DownloadBill: React.FC = () => { 近一周 { selectDateRange("month"); }} @@ -204,8 +236,9 @@ const DownloadBill: React.FC = () => { 近一月 { selectDateRange("custom"); }} @@ -219,16 +252,23 @@ const DownloadBill: React.FC = () => { {dateRange.start}{dateRange.end} )} - { - dateType === "custom" && ( - { setVisible(true); }}> - 时间范围 - - {dateRange.start && dateRange.end ? `${dateRange.start} 至 ${dateRange.end}` : "请选择账单时间"} - + {dateType === "custom" && ( + { + setVisible(true); + }} + > + 时间范围 + + + {dateRange.start && dateRange.end + ? `${dateRange.start} 至 ${dateRange.end}` + : "请选择账单时间"} + - ) - } + + )} { > 下载记录 - + {visible && ( { {`文件大小:7KB`} {`请输入交易密码`} - { - password.map((item, index) => ( - - {item} - - )) - } + {password.map((item, index) => ( + + {item} + + ))} - item !== "").join("")} maxlength={6} onInput={handlePasswordInput} /> + item !== "").join("")} + maxlength={6} + onInput={handlePasswordInput} + /> {/* 筛选账单弹窗 */} setShowFilterPopup(false)} - onConfirm={() => { }} + onClose={handleClose} + onConfirm={handleTypeConfirm} title="选择筛选项" className="filter_popup" > @@ -293,17 +340,13 @@ const DownloadBill: React.FC = () => { (option: Option) => ( { - set_load_transactions_params({ - ...load_transactions_params, - transaction_sub_type: option.value, - }); + setTransactionSubType(option.value); }} > {option.label} diff --git a/src/user_pages/edit/index.tsx b/src/user_pages/edit/index.tsx index 8d54365..a8c2819 100644 --- a/src/user_pages/edit/index.tsx +++ b/src/user_pages/edit/index.tsx @@ -1,50 +1,50 @@ -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 } from '@/services/userService'; -import { clear_login_state } from '@/services/loginService'; -import { convert_db_gender_to_display } from '@/utils/genderUtils'; -import { EditModal } from '@/components'; +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({ - id: '1', - nickname: '加载中...', - avatar: require('@/static/userInfo/default_avatar.svg'), - join_date: '加载中...', + id: "1", + nickname: "加载中...", + avatar: require("@/static/userInfo/default_avatar.svg"), + join_date: "加载中...", stats: { following: 0, friends: 0, hosted: 0, - participated: 0 + participated: 0, }, - personal_profile: '加载中...', - occupation: '加载中...', - ntrp_level: 'NTRP 3.0', - phone: '', - gender: '', - country: '', - province: '', - city: '', + 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: '' + nickname: "", + personal_profile: "", + occupation: "", + ntrp_level: "4.0", + phone: "", + gender: "", + birthday: "2000-01-01", + country: "", + province: "", + city: "", }); // 加载状态 @@ -52,18 +52,44 @@ const EditProfilePage: React.FC = () => { // 编辑弹窗状态 const [edit_modal_visible, setEditModalVisible] = useState(false); - const [editing_field, setEditingField] = useState(''); + const [editing_field, setEditingField] = useState(""); 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 [occupation_picker_visible, setOccupationPickerVisible] = + useState(false); + + // 职业数据 + const [professions, setProfessions] = useState([]); + + // 城市数据 + const [cities, setCities] = useState([]); // 页面加载时初始化数据 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 { @@ -71,23 +97,23 @@ const EditProfilePage: React.FC = () => { 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 || '' + 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); + console.error("加载用户信息失败:", error); Taro.showToast({ - title: '加载用户信息失败', - icon: 'error', - duration: 2000 + title: "加载用户信息失败", + icon: "error", + duration: 2000, }); } finally { setLoading(false); @@ -98,51 +124,51 @@ const EditProfilePage: React.FC = () => { const handle_avatar_upload = () => { Taro.chooseImage({ count: 1, - sizeType: ['compressed'], - sourceType: ['album', 'camera'], + 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 })); + setUserInfo((prev) => ({ ...prev, avatar: avatar_url })); Taro.showToast({ - title: '头像上传成功', - icon: 'success' + title: "头像上传成功", + icon: "success", }); } catch (error) { - console.error('头像上传失败:', error); + console.error("头像上传失败:", error); Taro.showToast({ - title: '头像上传失败', - icon: 'none' + title: "头像上传失败", + icon: "none", }); } - } + }, }); }; // 处理编辑弹窗 const handle_open_edit_modal = (field: string) => { - if (field === 'gender') { + if (field === "gender") { setGenderPickerVisible(true); return; } - if (field === 'birthday') { + if (field === "birthday") { setBirthdayPickerVisible(true); return; } - if (field === 'location') { + if (field === "location") { setLocationPickerVisible(true); return; } - if (field === 'ntrp_level') { + if (field === "ntrp_level") { setNtrpPickerVisible(true); return; } - if (field === 'occupation') { + if (field === "occupation") { setOccupationPickerVisible(true); return; } - if (field === 'nickname') { + if (field === "nickname") { // 手动输入 setEditingField(field); setEditModalVisible(true); @@ -159,60 +185,67 @@ const EditProfilePage: React.FC = () => { await UserService.update_user_info(update_data); // 更新本地状态 - setFormData(prev => ({ ...prev, [editing_field]: value })); - setUserInfo(prev => ({ ...prev, [editing_field]: value })); + setFormData((prev) => ({ ...prev, [editing_field]: value })); + setUserInfo((prev) => ({ ...prev, [editing_field]: value })); // 关闭弹窗 setEditModalVisible(false); - setEditingField(''); + setEditingField(""); // 显示成功提示 Taro.showToast({ - title: '保存成功', - icon: 'success' + title: "保存成功", + icon: "success", }); } catch (error) { - console.error('保存失败:', error); + console.error("保存失败:", error); Taro.showToast({ - title: '保存失败', - icon: 'error' + title: "保存失败", + icon: "error", }); } }; const handle_edit_modal_cancel = () => { setEditModalVisible(false); - setEditingField(''); + setEditingField(""); }; // 处理字段编辑 - const handle_field_edit = async (field: string | { [key: string]: string }, value?: string) => { + const handle_field_edit = async ( + field: string | { [key: string]: string }, + value?: string + ) => { try { - if (typeof field === 'object' && field !== null && !Array.isArray(field)) { + if ( + typeof field === "object" && + field !== null && + !Array.isArray(field) + ) { await UserService.update_user_info({ ...field }); // 更新本地状态 - setFormData(prev => ({ ...prev, ...field })); - setUserInfo(prev => ({ ...prev, ...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 })); + setFormData((prev) => ({ ...prev, [field as string]: value })); + setUserInfo((prev) => ({ ...prev, [field as string]: value })); } // 显示成功提示 Taro.showToast({ - title: '保存成功', - icon: 'success' + title: "保存成功", + icon: "success", }); } catch (error) { - console.error('保存失败:', error); + console.error("保存失败:", error); Taro.showToast({ - title: '保存失败', - icon: 'error' + title: "保存失败", + icon: "error", }); } }; @@ -220,13 +253,19 @@ const EditProfilePage: React.FC = () => { // 处理性别选择 const handle_gender_change = (e: any) => { const gender_value = e[0]; - handle_field_edit('gender', gender_value); + 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')}`); + handle_field_edit( + "birthday", + `${year}-${String(month).padStart(2, "0")}-${String(day).padStart( + 2, + "0" + )}` + ); }; // 处理地区选择 @@ -238,61 +277,66 @@ const EditProfilePage: React.FC = () => { // 处理NTRP水平选择 const handle_ntrp_level_change = (e: any) => { const ntrp_level_value = e[0]; - handle_field_edit('ntrp_level', ntrp_level_value); + handle_field_edit("ntrp_level", ntrp_level_value); }; // 处理职业选择 const handle_occupation_change = (e: any) => { const [country, province] = e; - handle_field_edit('occupation', `${country} ${province}`); + handle_field_edit("occupation", `${country} ${province}`); }; // 处理退出登录 const handle_logout = () => { Taro.showModal({ - title: '确认退出', - content: '确定要退出登录吗?', + title: "确认退出", + content: "确定要退出登录吗?", success: (res) => { if (res.confirm) { // 清除用户数据 clear_login_state(); Taro.reLaunch({ - url: '/login_pages/index/index' + url: "/login_pages/index/index", }); } - } + }, }); }; const onGetPhoneNumber = async (e) => { if (!e.detail || !e.detail.code) { Taro.showToast({ - title: '获取手机号失败,请重试', - icon: 'none', - duration: 2000 + title: "获取手机号失败,请重试", + icon: "none", + duration: 2000, }); return; } try { const phone = await UserService.parse_phone(e.detail.code); - handle_field_edit('phone', phone); + handle_field_edit("phone", phone); } catch (e) { - console.error('解析手机号失败:', e); + console.error("解析手机号失败:", e); Taro.showToast({ - title: '解析手机号失败,请重试', - icon: 'none', - duration: 2000 + title: "解析手机号失败,请重试", + icon: "none", + duration: 2000, }); } - } + }; return ( {/* 导航栏 */} - { Taro.navigateBack() }}> + { + Taro.navigateBack(); + }} + > { - - {/* 基本信息编辑 */} {/* 名字 */} - handle_open_edit_modal('nickname')}> + handle_open_edit_modal("nickname")} + > - + 名字 - {form_data.nickname || '188的王晨'} - + + {form_data.nickname || "188的王晨"} + + @@ -342,14 +395,25 @@ const EditProfilePage: React.FC = () => { {/* 性别 */} - handle_open_edit_modal('gender')}> + handle_open_edit_modal("gender")} + > - + 性别 - {convert_db_gender_to_display(form_data.gender)} - + + {convert_db_gender_to_display(form_data.gender)} + + @@ -357,14 +421,23 @@ const EditProfilePage: React.FC = () => { {/* 生日 */} - handle_open_edit_modal('birthday')}> + handle_open_edit_modal("birthday")} + > - + 生日 {form_data.birthday} - + @@ -373,16 +446,26 @@ const EditProfilePage: React.FC = () => { {/* 简介编辑 */} - handle_open_edit_modal('personal_profile')}> + handle_open_edit_modal("personal_profile")} + > - + 简介 - {form_data.personal_profile.replace(/\n/g, ' ') || '介绍一下自己'} + {form_data.personal_profile.replace(/\n/g, " ") || + "介绍一下自己"} - + @@ -392,40 +475,67 @@ const EditProfilePage: React.FC = () => { {/* 地区 */} - handle_open_edit_modal('location')}> + handle_open_edit_modal("location")} + > - + 地区 {`${form_data.country} ${form_data.province} ${form_data.city}`} - + {/* NTRP水平 */} - handle_open_edit_modal('ntrp_level')}> + handle_open_edit_modal("ntrp_level")} + > - + NTRP 水平 {form_data.ntrp_level} - + {/* 职业 */} - handle_open_edit_modal('occupation')}> + handle_open_edit_modal("occupation")} + > - + 职业 {form_data.occupation} - + @@ -436,7 +546,10 @@ const EditProfilePage: React.FC = () => { - + 手机 @@ -448,8 +561,17 @@ const EditProfilePage: React.FC = () => { onInput={handle_phone_input} onBlur={handle_phone_blur} /> */} - - + + @@ -477,63 +599,94 @@ const EditProfilePage: React.FC = () => { {/* 性别选择弹窗 */} - {gender_picker_visible && } + {gender_picker_visible && ( + + )} {/* 生日选择弹窗 */} - {birthday_picker_visible && } + {birthday_picker_visible && ( + + )} {/* 地区选择弹窗 */} - {location_picker_visible && } + {location_picker_visible && ( + + )} {/* NTRP水平选择弹窗 */} - {ntrp_picker_visible && } + {ntrp_picker_visible && ( + + )} {/* 职业选择弹窗 */} - {occupation_picker_visible && } + {occupation_picker_visible && ( + + )} ); };