diff --git a/src/components/index.ts b/src/components/index.ts index 99e68a0..61e979b 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -10,6 +10,7 @@ export { default as Logout } from './user/Logout'; export { default as ChartUploader } from './user/ChartUploader'; export { default as AvatarUploader } from './user/AvatarUploader'; export { default as IntroUploader } from './user/IntroUploader'; +export { default as NicknameEditor } from './user/NicknameEditor'; // UI组件 export { default as MajdataLogo } from './header/MajdataLogo'; diff --git a/src/components/user/NicknameEditor.tsx b/src/components/user/NicknameEditor.tsx new file mode 100644 index 0000000..133287c --- /dev/null +++ b/src/components/user/NicknameEditor.tsx @@ -0,0 +1,75 @@ +import { useState } from 'react'; +import { toast } from 'react-toastify'; +import axios from 'axios'; +import { endpoints } from '@/config/api'; +import { useLoc, useUserContext } from '@/hooks'; +import { getDisplayMessage } from '@/utils'; + +export default function NicknameEditor() { + const loc = useLoc(); + const { user } = useUserContext(); + const [nickname, setNickname] = useState(user?.nickname || ''); + const [isUploading, setIsUploading] = useState(false); + + async function onSubmit(event: React.FormEvent) { + event.preventDefault(); + + const formData = new FormData(event.currentTarget); + const content = formData.get('content') as string; + + if (!content || content.trim() === '') { + toast.error(loc('NicknameRequired', 'Please enter a nickname')); + return; + } + + const uploading = toast.loading(loc('Uploading'), { + hideProgressBar: false, + }); + + setIsUploading(true); + + try { + await axios.post(endpoints.account.nickname, { content }, { + withCredentials: true, + }); + toast.success(loc('UploadSuccess', 'Nickname updated successfully')); + } catch (e: unknown) { + const error = e as { response?: { data?: unknown }; message?: string }; + const message = getDisplayMessage( + error.response?.data ?? error.message, + loc('UploadFailed', 'Update failed'), + ); + toast.error(message, { autoClose: false }); + } finally { + toast.done(uploading); + setIsUploading(false); + } + } + + return ( +
+

+ {loc('NicknameHint', 'Your display name shown across the site.')} +

+
+ setNickname(e.target.value)} + disabled={isUploading} + /> + +
+
+ ); +} diff --git a/src/config/api.ts b/src/config/api.ts index c0f9a59..c41f335 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -23,6 +23,7 @@ export const endpoints = { diff: `${apiroot3}/account/favorite/collection/diff`, }, account: { + nickname: `${apiroot3}/account/nickname/`, info: `${apiroot3}/account/info/`, login: `${apiroot3}/account/Login`, logout: `${apiroot3}/account/Logout`, diff --git a/src/pages/user/ProfilePage.tsx b/src/pages/user/ProfilePage.tsx index e01b407..f2c2380 100644 --- a/src/pages/user/ProfilePage.tsx +++ b/src/pages/user/ProfilePage.tsx @@ -1,4 +1,4 @@ -import { PageLayout, AvatarUploader, IntroUploader } from '@/components'; +import { PageLayout, AvatarUploader, IntroUploader, NicknameEditor } from '@/components'; import { useLoc } from '@/hooks'; import { motion, type Variants } from 'framer-motion'; @@ -29,6 +29,18 @@ export default function UserProfilePage() { variants={slideInUp} >
+ {/* Nickname Card */} +
+
+
+ {loc('Nickname', 'Nickname')} +
+
+
+ +
+
+ {/* Avatar Settings Card */}
diff --git a/src/types/user.ts b/src/types/user.ts index 5fcec88..99fd283 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -6,5 +6,7 @@ export interface UserInfo { username: string; email?: string; + joinDate: string; + nickname: string; // 根据实际API返回添加更多字段 }