| "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 { useLocalStorage } from "../../hook/useLocalStorage"; |
| // 样式 |
| import "./user-avatar.scss"; |
| |
| interface User { |
| Id: number; |
| Avatar: string, |
| } |
| |
| // 用户下拉框 |
| export default function UserAvatar() { |
| const user = useLocalStorage<User>("user"); |
| const userId: number = user?.Id ?? -1; |
| // 功能选项 |
| 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`, { |
| body: { |
| userId, |
| 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, 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`, { |
| userId, |
| avatar: avatarUrl, |
| }); |
| toast.current?.show({ |
| severity: "success", |
| summary: "success", |
| detail: "修改头像成功", |
| }); |
| setAvatar(avatarUrl); |
| setShowEditAvatar(false); |
| localStorage.setItem("user", JSON.stringify({...user, Avatar: avatarUrl})) |
| } 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={useLocalStorage<User>("user")?.Avatar} |
| size="large" |
| shape="circle" |
| className="user-avatar-link" |
| /> |
| </Link> |
| |
| <OverlayPanel ref={op} dismissable={false}> |
| <div |
| onMouseEnter={() => clearTimeout(hoverTimeout)} |
| onMouseLeave={handleMouseLeave} |
| className="user-overlay-panel" |
| > |
| <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`, formData); |
| |
| const fileUrl = res.data; |
| 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> |
| </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`, |
| formData |
| ); |
| const fileUrl = res.data; |
| 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> |
| ); |
| } |