feat: implement logout on 401 response and update query handling
refactor: replace Button with div for consistent styling in AIEditorLink refactor: update navigation handling in AppVersionList and remove LayoutMain wrapper refactor: remove unused LayoutMain imports and components across various pages fix: ensure user app list is set correctly in useUserAppStore fix: update login URL format in AuthProvider fix: adjust layout styles in EnvPage and other pages for better responsiveness chore: update route definitions and create new routes for apps, config, domain, flowme, org, remote, token, user, and users style: replace Button with div for delete confirmation in various components fix: ensure correct handling of user profile image source
This commit is contained in:
@@ -1,11 +1,10 @@
|
||||
'use strict';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useLayoutStore } from './store';
|
||||
import clsx from 'clsx';
|
||||
import { toast as message } from 'sonner';
|
||||
import { useMemo } from 'react';
|
||||
import { queryLogin } from '../query';
|
||||
import { LogOut, Map, SquareUser, Users, X, ArrowDownLeftFromSquareIcon } from 'lucide-react';
|
||||
import { LogOut, Users, X, ArrowDownLeftFromSquareIcon } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -14,11 +13,12 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip';
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerDescription,
|
||||
} from '@/components/ui/drawer';
|
||||
import { openLink } from '../basename';
|
||||
|
||||
export const LayoutUser = () => {
|
||||
@@ -87,92 +87,82 @@ export const LayoutUser = () => {
|
||||
}, [store.me]);
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<div className={clsx('w-full h-full absolute z-20 no-drag text-primary', !open && 'hidden')}>
|
||||
<div
|
||||
className='w-full absolute h-full opacity-60 z-0'
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}></div>
|
||||
<div className='w-[400px] bg-white transition-all duration-300 h-full absolute top-0 right-0 rounded-l-lg'>
|
||||
<div className='flex justify-between p-6 mt-4 font-bold items-center border-b'>
|
||||
<div className='flex items-center gap-2'>
|
||||
用户: <span className='text-primary'>{store.me?.username}</span>
|
||||
</div>
|
||||
<div className='flex gap-4'>
|
||||
<Drawer open={open} onOpenChange={setOpen} direction="right">
|
||||
<DrawerContent className="w-100">
|
||||
<DrawerHeader className="border-b">
|
||||
<div className="flex items-center justify-between">
|
||||
<DrawerTitle className="flex items-center gap-2">
|
||||
用户: <span className="text-primary">{store.me?.username}</span>
|
||||
</DrawerTitle>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
title="退出登录"
|
||||
onClick={async () => {
|
||||
const res = await queryLogin.logout();
|
||||
if (res.code === 200) {
|
||||
const url = new URL(location.origin);
|
||||
url.pathname = '/root/login/';
|
||||
openLink(url.toString(), '_self');
|
||||
} else {
|
||||
message.error(res.message || '退出失败');
|
||||
}
|
||||
}}>
|
||||
<LogOut size={18} />
|
||||
</Button>
|
||||
{items.length > 0 && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<Button variant='ghost' size='icon'>
|
||||
<Users />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{items.map((item, index) => (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
onClick={() => {
|
||||
store.switchOrg(item.key, 'org');
|
||||
}}>
|
||||
<div className='mr-2'>{item.icon}</div>
|
||||
<div>{item.label}</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>切换组织</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
title="切换组织"
|
||||
className="inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-9 w-9">
|
||||
<Users size={18} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{items.map((item, index) => (
|
||||
<DropdownMenuItem
|
||||
key={index}
|
||||
onClick={() => {
|
||||
store.switchOrg(item.key);
|
||||
}}>
|
||||
<div className="mr-2">{item.icon}</div>
|
||||
<div>{item.label}</div>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
|
||||
<Button variant='ghost' size='icon' onClick={() => setOpen(false)}>
|
||||
<X />
|
||||
<Button variant="ghost" size="icon" onClick={() => setOpen(false)}>
|
||||
<X size={18} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-3 font-medium'>
|
||||
{menu.map((item, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className='flex items-center p-4 hover:bg-secondary hover:text-white cursor-pointer'
|
||||
onClick={() => {
|
||||
if (item.link) {
|
||||
openLink(item.link, '_self');
|
||||
setOpen(false);
|
||||
} else {
|
||||
message.info('即将上线');
|
||||
}
|
||||
}}>
|
||||
<div className='mr-4'>{item.icon}</div>
|
||||
<div>{item.title}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div
|
||||
className='flex items-center p-4 hover:bg-secondary hover:text-white cursor-pointer'
|
||||
onClick={async () => {
|
||||
const res = await queryLogin.logout();
|
||||
if (res.success) {
|
||||
const url = new URL(location.origin);
|
||||
url.pathname = '/root/login';
|
||||
openLink(url.toString(), '_self');
|
||||
} else {
|
||||
message.error(res.message || '退出失败');
|
||||
}
|
||||
}}>
|
||||
<div className='mr-4'>
|
||||
<LogOut size={16} />
|
||||
</div>
|
||||
<div>退出登录</div>
|
||||
<DrawerDescription className="sr-only">
|
||||
用户信息和账户管理
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex-1">
|
||||
{menu.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center px-4 py-3 hover:bg-secondary hover:text-secondary-foreground cursor-pointer transition-colors"
|
||||
onClick={() => {
|
||||
if (item.link) {
|
||||
openLink(item.link, '_self');
|
||||
setOpen(false);
|
||||
} else {
|
||||
message.info('即将上线');
|
||||
}
|
||||
}}>
|
||||
<div className="mr-3">{item.icon}</div>
|
||||
<div className="font-medium">{item.title}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -57,10 +57,7 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
|
||||
useLayoutEffect(() => {
|
||||
platformStore.init();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
menuStore.getMe();
|
||||
console.log('menuStore', menuStore.me);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -76,7 +73,6 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
<div className='flex items-center gap-2 text-sm '>
|
||||
{quickMenu.map((item, index) => {
|
||||
const isActive = location.pathname === item.link;
|
||||
console.log('isActive', location, item.link, isActive);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
@@ -114,7 +110,7 @@ export const LayoutMain = (props: LayoutMainProps) => {
|
||||
<TooltipTrigger>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
menuStore.switchOrg('', 'user');
|
||||
menuStore.switchOrg('');
|
||||
}}>
|
||||
<SwapOutlined />
|
||||
</IconButton>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { query, queryLogin } from '@/modules/query';
|
||||
import { create } from 'zustand';
|
||||
import { toast as message } from 'sonner';
|
||||
import { useLayoutStore } from '@/pages/auth/store';
|
||||
export const getIsMac = async () => {
|
||||
// @ts-ignore
|
||||
const userAgentData = navigator.userAgentData;
|
||||
@@ -68,38 +69,4 @@ export type LayoutStore = {
|
||||
setIsAdmin: (isAdmin: boolean) => void;
|
||||
checkHasOrg: () => boolean;
|
||||
};
|
||||
export const useLayoutStore = create<LayoutStore>((set, get) => ({
|
||||
open: false,
|
||||
setOpen: (open) => set({ open }),
|
||||
me: {},
|
||||
setMe: (me) => set({ me }),
|
||||
getMe: async () => {
|
||||
const res = await queryLogin.getMe();
|
||||
if (res.code === 200) {
|
||||
set({ me: res.data });
|
||||
set({ isAdmin: res.data.orgs?.includes('admin') });
|
||||
}
|
||||
},
|
||||
openUser: false,
|
||||
setOpenUser: (openUser) => set({ openUser }),
|
||||
switchOrg: async (username?: string, type?: string) => {
|
||||
const res = await queryLogin.switchUser(username || '');
|
||||
if (res.code === 200) {
|
||||
message.success('Switch success');
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
message.error(res.message || 'Request failed');
|
||||
}
|
||||
},
|
||||
isAdmin: false,
|
||||
setIsAdmin: (isAdmin) => set({ isAdmin }),
|
||||
checkHasOrg: () => {
|
||||
const user = get().me || {};
|
||||
if (!user.orgs) {
|
||||
return false;
|
||||
}
|
||||
return user?.orgs?.length > 0;
|
||||
},
|
||||
}));
|
||||
export { useLayoutStore }
|
||||
Reference in New Issue
Block a user