feat: 重构用户菜单组件,引入新的对话框组件以替代链接跳转方式,优化用户交互体验。

This commit is contained in:
zyh 2024-10-22 07:35:36 +00:00
parent df93f0d897
commit 26fa196bdb
4 changed files with 151 additions and 68 deletions

View File

@ -0,0 +1,38 @@
import { Dialog, DialogTitle, DialogDescription, DialogRoot } from '~/components/ui/Dialog';
import { useAuth } from '~/hooks/useAuth';
interface ProfileDialogProps {
isOpen: boolean;
onClose: () => void;
}
export function ProfileDialog({ isOpen, onClose }: ProfileDialogProps) {
const { user } = useAuth();
if (!user) return null;
return (
<DialogRoot open={isOpen}>
<Dialog onBackdrop={onClose} onClose={onClose}>
<DialogTitle></DialogTitle>
<DialogDescription>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<p className="mt-1 text-bolt-elements-textSecondary">{user.nickname}</p>
</div>
<div>
<label className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<p className="mt-1 text-bolt-elements-textSecondary">{user.phone}</p>
</div>
{/* 可以根据需要添加更多用户信息 */}
</div>
</DialogDescription>
</Dialog>
</DialogRoot>
);
}

View File

@ -0,0 +1,24 @@
import { Dialog, DialogTitle, DialogDescription, DialogRoot } from '~/components/ui/Dialog';
interface SubscriptionDialogProps {
isOpen: boolean;
onClose: () => void;
}
export function SubscriptionDialog({ isOpen, onClose }: SubscriptionDialogProps) {
return (
<DialogRoot open={isOpen}>
<Dialog onBackdrop={onClose} onClose={onClose}>
<DialogTitle></DialogTitle>
<DialogDescription>
<div className="space-y-4">
<p className="text-bolt-elements-textSecondary">
</p>
{/* 可以添加更多订阅相关的信息 */}
</div>
</DialogDescription>
</Dialog>
</DialogRoot>
);
}

View File

@ -1,74 +1,82 @@
import { Menu, Transition } from '@headlessui/react'; import { Menu, Transition } from '@headlessui/react';
import { Fragment } from 'react'; import { Fragment, useState } from 'react';
import { useAuth } from '~/hooks/useAuth'; import { useAuth } from '~/hooks/useAuth';
import { Avatar } from '~/components/ui/Avatar'; import { Avatar } from '~/components/ui/Avatar';
import { Link } from '@remix-run/react'; import { ProfileDialog } from '~/components/auth/ProfileDialog';
import { SubscriptionDialog } from '~/components/auth/SubscriptionDialog';
export function UserMenu() { export function UserMenu() {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const [isProfileOpen, setIsProfileOpen] = useState(false);
const [isSubscriptionOpen, setIsSubscriptionOpen] = useState(false);
if (!user) return null; if (!user) return null;
return ( return (
<Menu as="div" className="relative inline-block text-left"> <>
<div> <Menu as="div" className="relative inline-block text-left">
<Menu.Button className="inline-flex items-center gap-x-1.5 rounded-md bg-bolt-elements-background-depth-2 px-3 py-2 text-sm font-medium text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-primary-background"> <div>
<Avatar src={user.avatarUrl} alt={user.nickname} /> <Menu.Button className="inline-flex items-center gap-x-1.5 rounded-md bg-bolt-elements-background-depth-1 px-3 py-2 text-sm font-medium text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-2 transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-primary-background">
<span className="ml-2">{user.nickname}</span> <Avatar src={user.avatarUrl} alt={user.nickname} />
<div className="i-ph:caret-down-fill text-bolt-elements-textSecondary" aria-hidden="true" /> <span className="ml-2">{user.nickname}</span>
</Menu.Button> <div className="i-ph:caret-down-fill text-bolt-elements-textSecondary" aria-hidden="true" />
</div> </Menu.Button>
</div>
<Transition <Transition
as={Fragment} as={Fragment}
enter="transition ease-out duration-100" enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95" enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100" enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75" leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100" leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95" leaveTo="transform opacity-0 scale-95"
> >
<Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-bolt-elements-background-depth-2 shadow-lg ring-1 ring-bolt-elements-borderColor focus:outline-none"> <Menu.Items className="absolute right-0 z-10 mt-2 w-56 origin-top-right rounded-md bg-bolt-elements-background-depth-1 shadow-lg ring-1 ring-bolt-elements-borderColor focus:outline-none">
<div className="py-1"> <div className="py-1">
<Menu.Item> <Menu.Item>
{({ active }) => ( {({ active }) => (
<Link <button
to="/profile" onClick={() => setIsProfileOpen(true)}
className={`${ className={`${
active ? 'bg-bolt-elements-button-primary-background text-bolt-elements-button-primary-text' : 'text-bolt-elements-textSecondary' active ? 'bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary' : 'text-bolt-elements-textSecondary'
} block px-4 py-2 text-sm transition-colors duration-150 ease-in-out`} } block w-full text-left px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
> >
</Link> </button>
)} )}
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
{({ active }) => ( {({ active }) => (
<Link <button
to="/subscription" onClick={() => setIsSubscriptionOpen(true)}
className={`${ className={`${
active ? 'bg-bolt-elements-button-primary-background text-bolt-elements-button-primary-text' : 'text-bolt-elements-textSecondary' active ? 'bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary' : 'text-bolt-elements-textSecondary'
} block px-4 py-2 text-sm transition-colors duration-150 ease-in-out`} } block w-full text-left px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
> >
</Link> </button>
)} )}
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
{({ active }) => ( {({ active }) => (
<button <button
onClick={logout} onClick={logout}
className={`${ className={`${
active ? 'bg-bolt-elements-button-primary-background text-bolt-elements-button-primary-text' : 'text-bolt-elements-textSecondary' active ? 'bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary' : 'text-bolt-elements-textSecondary'
} block w-full text-left px-4 py-2 text-sm transition-colors duration-150 ease-in-out`} } block w-full text-left px-4 py-2 text-sm transition-colors duration-150 ease-in-out`}
> >
退 退
</button> </button>
)} )}
</Menu.Item> </Menu.Item>
</div> </div>
</Menu.Items> </Menu.Items>
</Transition> </Transition>
</Menu> </Menu>
<ProfileDialog isOpen={isProfileOpen} onClose={() => setIsProfileOpen(false)} />
<SubscriptionDialog isOpen={isSubscriptionOpen} onClose={() => setIsSubscriptionOpen(false)} />
</>
); );
} }

View File

@ -15,13 +15,25 @@ export function useAuth() {
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
const token = localStorage.getItem('token'); const checkAuth = () => {
const storedUser = localStorage.getItem('user'); const token = localStorage.getItem('token');
if (token && storedUser) { const storedUser = localStorage.getItem('user');
setIsAuthenticated(true); if (token && storedUser) {
setUser(JSON.parse(storedUser)); setIsAuthenticated(true);
} setUser(JSON.parse(storedUser));
setIsLoading(false); } else {
setIsAuthenticated(false);
setUser(null);
}
setIsLoading(false);
};
checkAuth();
window.addEventListener('storage', checkAuth);
return () => {
window.removeEventListener('storage', checkAuth);
};
}, []); }, []);
const login = (token: string, userData: User) => { const login = (token: string, userData: User) => {
@ -36,6 +48,7 @@ export function useAuth() {
localStorage.removeItem('user'); localStorage.removeItem('user');
setIsAuthenticated(false); setIsAuthenticated(false);
setUser(null); setUser(null);
navigate('/');
}; };
return { isAuthenticated, isLoading, user, login, logout }; return { isAuthenticated, isLoading, user, login, logout };