add user dropdown
Change-Id: I4c7b259ea7bd9b5c6ea2c8566b2ca08c5b2e6647
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index e29b4f6..3b52378 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,9 +1,9 @@
// app/layout.tsx
import type { Metadata } from 'next';
-import { Avatar } from 'primereact/avatar';
// 页面跳转
import Link from 'next/link';
+import UserAvatar from './user/component/userAvatar';
// PrimeReact 依赖
import { PrimeReactProvider } from 'primereact/api';
import 'primeicons/primeicons.css';
@@ -69,9 +69,7 @@
<span>通知</span>
</div>
</Link>
- <Link href="/user" className="no-underline">
- <Avatar image="/images/avatar/asiyajavayant.png" size="large" shape="circle" />
- </Link>
+ <UserAvatar />
</div>
</header>
<main className="mainContent">{children}</main>
diff --git a/src/app/user/component/user-avatar.scss b/src/app/user/component/user-avatar.scss
new file mode 100644
index 0000000..16f872d
--- /dev/null
+++ b/src/app/user/component/user-avatar.scss
@@ -0,0 +1,41 @@
+.user-avatar-wrapper {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+}
+
+.user-avatar-link {
+ cursor: pointer;
+}
+
+.user-overlay-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ padding: 0.5rem;
+}
+
+.dialog-container {
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ text-align: center;
+ margin-top: 2rem;
+}
+
+.dialog-input-group {
+
+ .p-float-label {
+ width: 100%;
+ }
+
+ .p-inputtext {
+ width: 100%;
+ }
+}
+
+.dialog-button-group {
+ .p-button {
+ margin-right: 1rem;
+ }
+}
diff --git a/src/app/user/component/userAvatar.tsx b/src/app/user/component/userAvatar.tsx
new file mode 100644
index 0000000..4ed94d7
--- /dev/null
+++ b/src/app/user/component/userAvatar.tsx
@@ -0,0 +1,282 @@
+'use client';
+
+import { useRef, useState } from 'react';
+import { Avatar } from 'primereact/avatar';
+import { Button } from 'primereact/button';
+// 弹窗
+import { Dialog } from 'primereact/dialog';
+// 头像下拉框
+import { OverlayPanel } from 'primereact/overlaypanel';
+// 输入框
+import { FloatLabel } from "primereact/floatlabel";
+import { InputText } from 'primereact/inputtext';
+// 页面跳转
+import Link from 'next/link';
+// 文件上传
+import { FileUpload } from 'primereact/fileupload';
+// 通知
+import { Toast } from 'primereact/toast';
+// 接口传输
+import axios from 'axios';
+// 样式
+import './user-avatar.scss';
+
+// 用户下拉框
+export default function UserAvatar() {
+ // 功能选项
+ const op = useRef<OverlayPanel>(null);
+ let hoverTimeout: NodeJS.Timeout;
+ // 通知
+ const toast = useRef<Toast>(null);
+ // 控制三个弹窗可见性
+ const [showEditSignature, setShowEditSignature] = useState(false);
+ const [showEditAvatar, setShowEditAvatar] = useState(false);
+ const [showEditPassword, setShowEditPassword] = useState(false);
+ // 头像URL
+ const [avatarUrl, setAvatar] = useState<string>('');
+ // 签名
+ const [signValue, setSignValue] = useState<string>('');
+ // 新密码
+ const [passwardValue, setPasswardValue] = useState<string>('');
+ const [newPasswardValue, setNewPasswardValue] = useState<string>('');
+ // 老密码
+ const [oldPasswardValue, setOldPasswardValue] = useState<string>('');
+
+
+ const handleMouseEnter = (event: React.MouseEvent) => {
+ clearTimeout(hoverTimeout);
+ op.current?.show(event, event.currentTarget);
+ };
+
+ const handleMouseLeave = () => {
+ hoverTimeout = setTimeout(() => {
+ op.current?.hide();
+ }, 300);
+ };
+
+
+ // 修改密码接口
+ const editPassward = async () => {
+ try {
+ await axios.put(process.env.PUBLIC_URL + `/user/password`, {
+ params: { userId: 22301145, password: oldPasswardValue, newPassword: passwardValue }
+ });
+ toast.current?.show({ severity: 'success', summary: 'success', detail: '修改密码成功' });
+ setShowEditPassword(false);
+ } catch (err) {
+ console.error('修改密码失败', err);
+ toast.current?.show({ severity: 'error', summary: 'error', detail: '修改密码失败' });
+ }
+ }
+ // 修改签名接口
+ const editSign = async () => {
+ try {
+ await axios.put(process.env.PUBLIC_URL + `/user/signature`, {
+ params: { userId: 22301145, signature: signValue }
+ });
+ toast.current?.show({ severity: 'success', summary: 'success', detail: '修改签名成功' });
+ setShowEditSignature(false);
+ } catch (err) {
+ console.error('修改签名失败', err);
+ toast.current?.show({ severity: 'error', summary: 'error', detail: '修改签名失败' });
+ }
+ }
+
+ // 修改头像接口
+ const editAvatar = async () => {
+ try {
+ await axios.put(process.env.PUBLIC_URL + `/user/avatar`, {
+ params: { userId: 22301145, avatar: avatarUrl }
+ });
+ toast.current?.show({ severity: 'success', summary: 'success', detail: '修改头像成功' });
+ setShowEditAvatar(false);
+ } catch (err) {
+ console.error('修改头像失败', err);
+ toast.current?.show({ severity: 'error', summary: 'error', detail: '修改头像失败' });
+ }
+ }
+ return (
+ <div
+ onMouseEnter={handleMouseEnter}
+ onMouseLeave={handleMouseLeave}
+ className="user-avatar-wrapper"
+ >
+ <Toast ref={toast}></Toast>
+ <Link href="/user" className="no-underline">
+ <Avatar
+ image="/images/avatar/asiyajavayant.png"
+ size="large"
+ shape="circle"
+ className="user-avatar-link"
+ />
+ </Link>
+
+ <OverlayPanel ref={op} dismissable={false}>
+ <div
+ onMouseEnter={() => clearTimeout(hoverTimeout)}
+ onMouseLeave={handleMouseLeave}
+ className="user-overlay-panel"
+ >
+ <Button
+ text
+ icon="pi pi-pencil"
+ label="修改签名"
+ onClick={() => setShowEditSignature(true)}
+ />
+ <Button
+ text
+ icon="pi pi-image"
+ label="修改头像"
+ onClick={() => setShowEditAvatar(true)}
+ />
+ <Button
+ text
+ icon="pi pi-unlock"
+ label="修改密码"
+ onClick={() => setShowEditPassword(true)}
+ />
+ </div>
+ </OverlayPanel>
+
+ {/* 修改签名弹窗 */}
+ <Dialog
+ header="修改签名"
+ visible={showEditSignature}
+ style={{ width: '30vw' }}
+ onHide={() => {
+ setSignValue('');
+ setShowEditSignature(false);
+ }}
+ modal
+ >
+ <div className="dialog-container">
+ <div className="dialog-input-group">
+ <FloatLabel>
+ <InputText id="username" value={signValue} onChange={(e) => setSignValue(e.target.value)} />
+ <label htmlFor="username">个性签名</label>
+ </FloatLabel>
+ </div>
+ <div className="dialog-button-group">
+ <Button label="确定" className="p-button-sm" onClick={() => editSign()} />
+ <Button
+ label="取消"
+ className="p-button-secondary p-button-sm"
+ onClick={() => {
+ setSignValue('');
+ setShowEditSignature(false);
+ }}
+ />
+ </div>
+ </div>
+ </Dialog>
+
+ {/* 修改头像弹窗 */}
+ <Dialog
+ header="修改头像"
+ visible={showEditAvatar}
+ style={{ display: 'flex', flexDirection: 'column', width: '30vw' }}
+ onHide={() => {
+ setAvatar('');
+ setShowEditAvatar(false);
+ }}
+ modal
+ >
+ <div className="dialog-container">
+ <FileUpload
+ mode="advanced"
+ name="file"
+ customUpload
+ uploadHandler={async (e) => {
+ const formData = new FormData();
+ formData.append("file", e.files[0]);
+
+ try {
+ const res = await axios.post(`${process.env.PUBLIC_URL}/file/avatar`, formData);
+
+ const fileUrl = res.data.url;
+ console.log(fileUrl);
+ setAvatar(fileUrl);
+ toast.current?.show({ severity: 'success', summary: '上传成功' });
+ } catch (error) {
+ console.log(error);
+ toast.current?.show({ severity: 'error', summary: '上传失败' });
+ }
+ }}
+ auto
+ accept="image/*"
+ chooseLabel="上传头像"
+ />
+
+ <div className="dialog-button-group">
+ <Button label="确定" className="p-button-sm" onClick={() => editAvatar()} />
+ <Button
+ label="取消"
+ className="p-button-secondary p-button-sm"
+ onClick={() => setShowEditAvatar(false)}
+ />
+ </div>
+ </div>
+ </Dialog>
+
+ {/* 修改密码弹窗 */}
+ <Dialog
+ header="修改密码"
+ visible={showEditPassword}
+ style={{ width: '30vw' }}
+ onHide={() => {
+ setOldPasswardValue('');
+ setPasswardValue('');
+ setNewPasswardValue('');
+ setShowEditPassword(false);
+ }}
+ modal
+ >
+ <div className="dialog-container">
+ <div className="dialog-input-group">
+ <FloatLabel>
+ <InputText id="username" value={oldPasswardValue} onChange={(e) => setOldPasswardValue(e.target.value)} />
+ <label htmlFor="username">输入旧密码</label>
+ </FloatLabel>
+ </div>
+ <div className="dialog-input-group">
+ <FloatLabel>
+ <InputText id="username" value={passwardValue} onChange={(e) => setPasswardValue(e.target.value)} />
+ <label htmlFor="username">更新密码</label>
+ </FloatLabel>
+ </div>
+ <div className="dialog-input-group">
+ <FloatLabel>
+ <InputText id="username" value={newPasswardValue} onChange={(e) => setNewPasswardValue(e.target.value)} />
+ <label htmlFor="username">确认密码</label>
+ </FloatLabel>
+ </div>
+ <div className="dialog-button-group">
+ <Button label="确定" className="p-button-sm" onClick={() => {
+ if (passwardValue !== newPasswardValue) {
+ toast.current?.show({
+ severity: 'warn',
+ summary: '两次密码不一致',
+ detail: '请确保新密码和确认密码一致',
+ });
+ return;
+ } else {
+ editPassward();
+ }
+ }} />
+ <Button
+ label="取消"
+ className="p-button-secondary p-button-sm"
+ onClick={() => {
+ setOldPasswardValue('');
+ setPasswardValue('');
+ setNewPasswardValue('');
+ setShowEditPassword(false);
+ }}
+ />
+ </div>
+ </div>
+ </Dialog>
+ </div>
+ );
+
+}