11// @ts -nocheck
22import { useState , useEffect , useCallback , useMemo , useRef } from "react" ;
3- import { useNavigate } from "react-router-dom" ;
3+ import { useNavigate , Link } from "react-router-dom" ;
44import { getApiClient , Book , Collection } from "@bookdock/api-client" ;
55import { useAuthStore } from "../stores/authStore" ;
66import { getCoverImageUrl } from "../utils/network" ;
@@ -17,6 +17,11 @@ import {
1717 Search ,
1818 Clock ,
1919 User ,
20+ Settings ,
21+ Crown ,
22+ RefreshCw ,
23+ Shield ,
24+ LogOut ,
2025} from "lucide-react" ;
2126
2227function formatDate ( dateStr : string ) : string {
@@ -53,7 +58,7 @@ type TabKey = "collections" | "reading" | "favorites" | "downloads" | "notes";
5358
5459export default function Profile ( ) {
5560 const navigate = useNavigate ( ) ;
56- const { user } = useAuthStore ( ) ;
61+ const { user, logout } = useAuthStore ( ) ;
5762 const [ activeTab , setActiveTab ] = useState < TabKey > ( "collections" ) ;
5863 const [ collections , setCollections ] = useState < Collection [ ] > ( [ ] ) ;
5964 const [ favorites , setFavorites ] = useState < Book [ ] > ( [ ] ) ;
@@ -67,6 +72,21 @@ export default function Profile() {
6772 const noteLimit = 20 ;
6873 const [ showCreateModal , setShowCreateModal ] = useState ( false ) ;
6974 const [ newCollectionName , setNewCollectionName ] = useState ( "" ) ;
75+ const [ showDropdown , setShowDropdown ] = useState ( false ) ;
76+ const dropdownRef = useRef < HTMLDivElement > ( null ) ;
77+
78+ // Close dropdown on click outside
79+ useEffect ( ( ) => {
80+ const handleClickOutside = ( event : MouseEvent ) => {
81+ if ( dropdownRef . current && ! dropdownRef . current . contains ( event . target as Node ) ) {
82+ setShowDropdown ( false ) ;
83+ }
84+ } ;
85+ if ( showDropdown ) {
86+ document . addEventListener ( "mousedown" , handleClickOutside ) ;
87+ }
88+ return ( ) => document . removeEventListener ( "mousedown" , handleClickOutside ) ;
89+ } , [ showDropdown ] ) ;
7090
7191 const fetchData = useCallback ( async ( ) => {
7292 setIsLoading ( true ) ;
@@ -442,14 +462,69 @@ export default function Profile() {
442462 < div className = "space-y-6" >
443463 { /* Header */ }
444464 < div className = "flex items-center justify-between" >
445- < h1 className = "text-3xl font-bold text-gray-900 dark:text-white" > 我的</ h1 >
465+ < div className = "flex items-center gap-2" >
466+ < div className = "relative md:hidden" ref = { dropdownRef } >
467+ < button
468+ onClick = { ( ) => setShowDropdown ( ! showDropdown ) }
469+ className = "p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
470+ >
471+ < Plus className = "w-5 h-5" />
472+ </ button >
473+ { showDropdown && (
474+ < div className = "absolute left-0 top-full mt-1 w-48 bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 z-50 py-1" >
475+ < button
476+ onClick = { ( ) => { setShowCreateModal ( true ) ; setShowDropdown ( false ) ; } }
477+ className = "w-full flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
478+ >
479+ < Plus className = "w-4 h-4" />
480+ < span > 新建书单</ span >
481+ </ button >
482+ < Link
483+ to = "/membership"
484+ onClick = { ( ) => setShowDropdown ( false ) }
485+ className = "flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
486+ >
487+ < Crown className = "w-4 h-4" />
488+ < span > 会员中心</ span >
489+ </ Link >
490+ < Link
491+ to = "/notes"
492+ onClick = { ( ) => setShowDropdown ( false ) }
493+ className = "flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
494+ >
495+ < StickyNote className = "w-4 h-4" />
496+ < span > 笔记</ span >
497+ </ Link >
498+ { user ?. role === "admin" && (
499+ < Link
500+ to = "/admin"
501+ onClick = { ( ) => setShowDropdown ( false ) }
502+ className = "flex items-center gap-2 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
503+ >
504+ < Shield className = "w-4 h-4" />
505+ < span > 后台管理</ span >
506+ </ Link >
507+ ) }
508+ < div className = "mx-3 my-1 border-t border-gray-100 dark:border-gray-700" />
509+ < button
510+ onClick = { ( ) => { logout ( ) ; setShowDropdown ( false ) ; } }
511+ className = "w-full flex items-center gap-2 px-4 py-2.5 text-sm text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
512+ >
513+ < LogOut className = "w-4 h-4" />
514+ < span > 退出登录</ span >
515+ </ button >
516+ </ div >
517+ ) }
518+ </ div >
519+ < h1 className = "hidden md:block text-3xl font-bold text-gray-900 dark:text-white" > 我的</ h1 >
520+ </ div >
446521 < div className = "flex items-center gap-2" >
447522 < button
448- onClick = { ( ) => setShowCreateModal ( true ) }
449- className = "flex items-center gap-1 px-3 py-2 bg-blue-500 text-white rounded-lg text-sm hover:bg-blue-600 transition-colors"
523+ onClick = { ( ) => navigate ( "/settings" ) }
524+ className = "md:hidden p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
525+ title = "设置"
450526 >
451- < Plus className = "w-4 h-4" />
452- 新建书单
527+ < Settings className = "w-5 h-5" />
453528 </ button >
454529 </ div >
455530 </ div >
0 commit comments