blob: abc1b6625664da2662c37bb18064f09bff8541d5 [file] [log] [blame]
Seamherbb14ecb2025-06-09 22:37:20 +08001"use client";
LaoeGaoci656ab002025-06-05 17:48:28 +08002
Seamherbb14ecb2025-06-09 22:37:20 +08003import { useRef, useState } from "react";
4import { Avatar } from "primereact/avatar";
5import { Button } from "primereact/button";
LaoeGaoci656ab002025-06-05 17:48:28 +08006// 弹窗
Seamherbb14ecb2025-06-09 22:37:20 +08007import { Dialog } from "primereact/dialog";
LaoeGaoci656ab002025-06-05 17:48:28 +08008// 头像下拉框
Seamherbb14ecb2025-06-09 22:37:20 +08009import { OverlayPanel } from "primereact/overlaypanel";
LaoeGaoci656ab002025-06-05 17:48:28 +080010// 输入框
11import { FloatLabel } from "primereact/floatlabel";
Seamherbb14ecb2025-06-09 22:37:20 +080012import { InputText } from "primereact/inputtext";
LaoeGaoci656ab002025-06-05 17:48:28 +080013// 页面跳转
Seamherbb14ecb2025-06-09 22:37:20 +080014import Link from "next/link";
LaoeGaoci656ab002025-06-05 17:48:28 +080015// 文件上传
Seamherbb14ecb2025-06-09 22:37:20 +080016import { FileUpload } from "primereact/fileupload";
LaoeGaoci656ab002025-06-05 17:48:28 +080017// 通知
Seamherbb14ecb2025-06-09 22:37:20 +080018import { Toast } from "primereact/toast";
LaoeGaoci656ab002025-06-05 17:48:28 +080019// 接口传输
Seamherbb14ecb2025-06-09 22:37:20 +080020import axios from "axios";
21import { useLocalStorage } from "../../hook/useLocalStorage";
LaoeGaoci656ab002025-06-05 17:48:28 +080022// 样式
Seamherbb14ecb2025-06-09 22:37:20 +080023import "./user-avatar.scss";
24
LaoeGaocid0773912025-06-09 00:38:40 +080025interface User {
Seamherbb14ecb2025-06-09 22:37:20 +080026 Id: number;
27 Avatar: string,
LaoeGaocid0773912025-06-09 00:38:40 +080028}
Seamherbb14ecb2025-06-09 22:37:20 +080029
LaoeGaoci656ab002025-06-05 17:48:28 +080030// 用户下拉框
31export default function UserAvatar() {
Seamherbb14ecb2025-06-09 22:37:20 +080032 const user = useLocalStorage<User>("user");
33 const userId: number = user?.Id ?? -1;
34 // 功能选项
35 const op = useRef<OverlayPanel>(null);
36 let hoverTimeout: NodeJS.Timeout;
37 // 通知
38 const toast = useRef<Toast>(null);
39 // 控制三个弹窗可见性
40 const [showEditSignature, setShowEditSignature] = useState(false);
41 const [showEditAvatar, setShowEditAvatar] = useState(false);
42 const [showEditPassword, setShowEditPassword] = useState(false);
43 // 头像URL
44 const [avatarUrl, setAvatar] = useState<string>("");
45 // 签名
46 const [signValue, setSignValue] = useState<string>("");
47 // 新密码
48 const [passwardValue, setPasswardValue] = useState<string>("");
49 const [newPasswardValue, setNewPasswardValue] = useState<string>("");
50 // 老密码
51 const [oldPasswardValue, setOldPasswardValue] = useState<string>("");
LaoeGaocid0773912025-06-09 00:38:40 +080052
Seamherbb14ecb2025-06-09 22:37:20 +080053 const handleMouseEnter = (event: React.MouseEvent) => {
54 clearTimeout(hoverTimeout);
55 op.current?.show(event, event.currentTarget);
56 };
LaoeGaoci656ab002025-06-05 17:48:28 +080057
Seamherbb14ecb2025-06-09 22:37:20 +080058 const handleMouseLeave = () => {
59 hoverTimeout = setTimeout(() => {
60 op.current?.hide();
61 }, 300);
62 };
LaoeGaoci656ab002025-06-05 17:48:28 +080063
Seamherbb14ecb2025-06-09 22:37:20 +080064 // 修改密码接口
65 const editPassward = async () => {
66 try {
67 await axios.put(process.env.PUBLIC_URL + `/user/password`, {
68 body: {
69 userId,
70 password: oldPasswardValue,
71 newPassword: passwardValue,
72 },
73 });
74 toast.current?.show({
75 severity: "success",
76 summary: "success",
77 detail: "修改密码成功",
78 });
79 setShowEditPassword(false);
80 } catch (err) {
81 console.error("修改密码失败", err);
82 toast.current?.show({
83 severity: "error",
84 summary: "error",
85 detail: "修改密码失败",
86 });
LaoeGaoci656ab002025-06-05 17:48:28 +080087 }
Seamherbb14ecb2025-06-09 22:37:20 +080088 };
LaoeGaoci656ab002025-06-05 17:48:28 +080089
Seamherbb14ecb2025-06-09 22:37:20 +080090 // 修改签名接口
91 const editSign = async () => {
92 try {
93 await axios.put(process.env.PUBLIC_URL + `/user/signature`, {
94 params: { userId, signature: signValue },
95 });
96 toast.current?.show({
97 severity: "success",
98 summary: "success",
99 detail: "修改签名成功",
100 });
101 setShowEditSignature(false);
102 } catch (err) {
103 console.error("修改签名失败", err);
104 toast.current?.show({
105 severity: "error",
106 summary: "error",
107 detail: "修改签名失败",
108 });
LaoeGaoci656ab002025-06-05 17:48:28 +0800109 }
Seamherbb14ecb2025-06-09 22:37:20 +0800110 };
111
112 // 修改头像接口
113 const editAvatar = async () => {
114 try {
115 await axios.put(process.env.PUBLIC_URL + `/user/avatar`, {
116 userId,
117 avatar: avatarUrl,
118 });
119 toast.current?.show({
120 severity: "success",
121 summary: "success",
122 detail: "修改头像成功",
123 });
124 setAvatar(avatarUrl);
125 setShowEditAvatar(false);
126 localStorage.setItem("user", JSON.stringify({...user, Avatar: avatarUrl}))
127 } catch (err) {
128 console.error("修改头像失败", err);
129 toast.current?.show({
130 severity: "error",
131 summary: "error",
132 detail: "修改头像失败",
133 });
134 }
135 };
136 return (
137 <div
138 onMouseEnter={handleMouseEnter}
139 onMouseLeave={handleMouseLeave}
140 className="user-avatar-wrapper"
141 >
142 <Toast ref={toast}></Toast>
143 <Link href="/user" className="no-underline">
144 <Avatar
145 image={useLocalStorage<User>("user")?.Avatar}
146 size="large"
147 shape="circle"
148 className="user-avatar-link"
149 />
150 </Link>
151
152 <OverlayPanel ref={op} dismissable={false}>
LaoeGaoci656ab002025-06-05 17:48:28 +0800153 <div
Seamherbb14ecb2025-06-09 22:37:20 +0800154 onMouseEnter={() => clearTimeout(hoverTimeout)}
155 onMouseLeave={handleMouseLeave}
156 className="user-overlay-panel"
LaoeGaoci656ab002025-06-05 17:48:28 +0800157 >
158 <Toast ref={toast}></Toast>
159 <Link href="/user" className="no-underline">
160 <Avatar
161 image="/images/avatar/asiyajavayant.png"
162 size="large"
163 shape="circle"
164 className="user-avatar-link"
165 />
166 </Link>
167
168 <OverlayPanel ref={op} dismissable={false}>
169 <div
170 onMouseEnter={() => clearTimeout(hoverTimeout)}
171 onMouseLeave={handleMouseLeave}
172 className="user-overlay-panel"
173 >
174 <Button
175 text
176 icon="pi pi-pencil"
177 label="修改签名"
178 onClick={() => setShowEditSignature(true)}
179 />
180 <Button
181 text
182 icon="pi pi-image"
183 label="修改头像"
184 onClick={() => setShowEditAvatar(true)}
185 />
186 <Button
187 text
188 icon="pi pi-unlock"
189 label="修改密码"
190 onClick={() => setShowEditPassword(true)}
191 />
192 </div>
193 </OverlayPanel>
194
195 {/* 修改签名弹窗 */}
196 <Dialog
197 header="修改签名"
198 visible={showEditSignature}
199 style={{ width: '30vw' }}
200 onHide={() => {
201 setSignValue('');
202 setShowEditSignature(false);
203 }}
204 modal
205 >
206 <div className="dialog-container">
207 <div className="dialog-input-group">
208 <FloatLabel>
209 <InputText id="username" value={signValue} onChange={(e) => setSignValue(e.target.value)} />
210 <label htmlFor="username">个性签名</label>
211 </FloatLabel>
212 </div>
213 <div className="dialog-button-group">
214 <Button label="确定" className="p-button-sm" onClick={() => editSign()} />
215 <Button
216 label="取消"
217 className="p-button-secondary p-button-sm"
218 onClick={() => {
219 setSignValue('');
220 setShowEditSignature(false);
221 }}
222 />
223 </div>
224 </div>
225 </Dialog>
226
227 {/* 修改头像弹窗 */}
228 <Dialog
229 header="修改头像"
230 visible={showEditAvatar}
231 style={{ display: 'flex', flexDirection: 'column', width: '30vw' }}
232 onHide={() => {
233 setAvatar('');
234 setShowEditAvatar(false);
235 }}
236 modal
237 >
238 <div className="dialog-container">
239 <FileUpload
240 mode="advanced"
241 name="file"
242 customUpload
243 uploadHandler={async (e) => {
244 const formData = new FormData();
245 formData.append("file", e.files[0]);
246
247 try {
LaoeGaoci36b5bc82025-06-09 20:52:39 +0800248 const res = await axios.post(`${process.env.PUBLIC_URL}/file`, formData);
LaoeGaoci656ab002025-06-05 17:48:28 +0800249
Seamherbb14ecb2025-06-09 22:37:20 +0800250 const fileUrl = res.data;
LaoeGaoci656ab002025-06-05 17:48:28 +0800251 console.log(fileUrl);
252 setAvatar(fileUrl);
253 toast.current?.show({ severity: 'success', summary: '上传成功' });
254 } catch (error) {
255 console.log(error);
256 toast.current?.show({ severity: 'error', summary: '上传失败' });
257 }
258 }}
259 auto
260 accept="image/*"
261 chooseLabel="上传头像"
262 />
263
264 <div className="dialog-button-group">
265 <Button label="确定" className="p-button-sm" onClick={() => editAvatar()} />
266 <Button
267 label="取消"
268 className="p-button-secondary p-button-sm"
269 onClick={() => setShowEditAvatar(false)}
270 />
271 </div>
272 </div>
273 </Dialog>
274
275 {/* 修改密码弹窗 */}
276 <Dialog
277 header="修改密码"
278 visible={showEditPassword}
279 style={{ width: '30vw' }}
280 onHide={() => {
281 setOldPasswardValue('');
282 setPasswardValue('');
283 setNewPasswardValue('');
284 setShowEditPassword(false);
285 }}
286 modal
287 >
288 <div className="dialog-container">
289 <div className="dialog-input-group">
290 <FloatLabel>
291 <InputText id="username" value={oldPasswardValue} onChange={(e) => setOldPasswardValue(e.target.value)} />
292 <label htmlFor="username">输入旧密码</label>
293 </FloatLabel>
294 </div>
295 <div className="dialog-input-group">
296 <FloatLabel>
297 <InputText id="username" value={passwardValue} onChange={(e) => setPasswardValue(e.target.value)} />
298 <label htmlFor="username">更新密码</label>
299 </FloatLabel>
300 </div>
301 <div className="dialog-input-group">
302 <FloatLabel>
303 <InputText id="username" value={newPasswardValue} onChange={(e) => setNewPasswardValue(e.target.value)} />
304 <label htmlFor="username">确认密码</label>
305 </FloatLabel>
306 </div>
307 <div className="dialog-button-group">
308 <Button label="确定" className="p-button-sm" onClick={() => {
309 if (passwardValue !== newPasswardValue) {
310 toast.current?.show({
311 severity: 'warn',
312 summary: '两次密码不一致',
313 detail: '请确保新密码和确认密码一致',
314 });
315 return;
316 } else {
317 editPassward();
318 }
319 }} />
320 <Button
321 label="取消"
322 className="p-button-secondary p-button-sm"
323 onClick={() => {
324 setOldPasswardValue('');
325 setPasswardValue('');
326 setNewPasswardValue('');
327 setShowEditPassword(false);
328 }}
329 />
330 </div>
331 </div>
332 </Dialog>
333 </div>
Seamherbb14ecb2025-06-09 22:37:20 +0800334 </OverlayPanel>
LaoeGaoci656ab002025-06-05 17:48:28 +0800335
Seamherbb14ecb2025-06-09 22:37:20 +0800336 {/* 修改签名弹窗 */}
337 <Dialog
338 header="修改签名"
339 visible={showEditSignature}
340 style={{ width: "30vw" }}
341 onHide={() => {
342 setSignValue("");
343 setShowEditSignature(false);
344 }}
345 modal
346 >
347 <div className="dialog-container">
348 <div className="dialog-input-group">
349 <FloatLabel>
350 <InputText
351 id="username"
352 value={signValue}
353 onChange={(e) => setSignValue(e.target.value)}
354 />
355 <label htmlFor="username">个性签名</label>
356 </FloatLabel>
357 </div>
358 <div className="dialog-button-group">
359 <Button
360 label="确定"
361 className="p-button-sm"
362 onClick={() => editSign()}
363 />
364 <Button
365 label="取消"
366 className="p-button-secondary p-button-sm"
367 onClick={() => {
368 setSignValue("");
369 setShowEditSignature(false);
370 }}
371 />
372 </div>
373 </div>
374 </Dialog>
375
376 {/* 修改头像弹窗 */}
377 <Dialog
378 header="修改头像"
379 visible={showEditAvatar}
380 style={{ display: "flex", flexDirection: "column", width: "30vw" }}
381 onHide={() => {
382 setAvatar("");
383 setShowEditAvatar(false);
384 }}
385 modal
386 >
387 <div className="dialog-container">
388 <FileUpload
389 mode="advanced"
390 name="file"
391 customUpload
392 uploadHandler={async (e) => {
393 const formData = new FormData();
394 formData.append("file", e.files[0]);
395
396 try {
397 const res = await axios.post(
398 `${process.env.PUBLIC_URL}/file`,
399 formData
400 );
401 const fileUrl = res.data;
402 console.log(fileUrl);
403 setAvatar(fileUrl);
404 toast.current?.show({
405 severity: "success",
406 summary: "上传成功",
407 });
408 } catch (error) {
409 console.log(error);
410 toast.current?.show({ severity: "error", summary: "上传失败" });
411 }
412 }}
413 auto
414 accept="image/*"
415 chooseLabel="上传头像"
416 />
417
418 <div className="dialog-button-group">
419 <Button
420 label="确定"
421 className="p-button-sm"
422 onClick={() => editAvatar()}
423 />
424 <Button
425 label="取消"
426 className="p-button-secondary p-button-sm"
427 onClick={() => setShowEditAvatar(false)}
428 />
429 </div>
430 </div>
431 </Dialog>
432
433 {/* 修改密码弹窗 */}
434 <Dialog
435 header="修改密码"
436 visible={showEditPassword}
437 style={{ width: "30vw" }}
438 onHide={() => {
439 setOldPasswardValue("");
440 setPasswardValue("");
441 setNewPasswardValue("");
442 setShowEditPassword(false);
443 }}
444 modal
445 >
446 <div className="dialog-container">
447 <div className="dialog-input-group">
448 <FloatLabel>
449 <InputText
450 id="username"
451 value={oldPasswardValue}
452 onChange={(e) => setOldPasswardValue(e.target.value)}
453 />
454 <label htmlFor="username">输入旧密码</label>
455 </FloatLabel>
456 </div>
457 <div className="dialog-input-group">
458 <FloatLabel>
459 <InputText
460 id="username"
461 value={passwardValue}
462 onChange={(e) => setPasswardValue(e.target.value)}
463 />
464 <label htmlFor="username">更新密码</label>
465 </FloatLabel>
466 </div>
467 <div className="dialog-input-group">
468 <FloatLabel>
469 <InputText
470 id="username"
471 value={newPasswardValue}
472 onChange={(e) => setNewPasswardValue(e.target.value)}
473 />
474 <label htmlFor="username">确认密码</label>
475 </FloatLabel>
476 </div>
477 <div className="dialog-button-group">
478 <Button
479 label="确定"
480 className="p-button-sm"
481 onClick={() => {
482 if (passwardValue !== newPasswardValue) {
483 toast.current?.show({
484 severity: "warn",
485 summary: "两次密码不一致",
486 detail: "请确保新密码和确认密码一致",
487 });
488 return;
489 } else {
490 editPassward();
491 }
492 }}
493 />
494 <Button
495 label="取消"
496 className="p-button-secondary p-button-sm"
497 onClick={() => {
498 setOldPasswardValue("");
499 setPasswardValue("");
500 setNewPasswardValue("");
501 setShowEditPassword(false);
502 }}
503 />
504 </div>
505 </div>
506 </Dialog>
507 </div>
508 );
LaoeGaoci656ab002025-06-05 17:48:28 +0800509}