merge
This commit is contained in:
@@ -1,79 +1,91 @@
|
||||
import React from 'react';
|
||||
import { Layout, Switch, Button, Dropdown } from 'antd';
|
||||
import { UserOutlined, LogoutOutlined } from '@ant-design/icons';
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { MenuTrigger } from '../Layout/MenuTrigger';
|
||||
import React from "react";
|
||||
import { Layout, Switch, Button, Dropdown } from "antd";
|
||||
import { UserOutlined, LogoutOutlined } from "@ant-design/icons";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { MenuTrigger } from "../Layout/MenuTrigger";
|
||||
const { Header: AntHeader } = Layout;
|
||||
import { LuSun } from "react-icons/lu";
|
||||
import { GoMoon } from "react-icons/go";
|
||||
|
||||
const Header = ({ collapsed, setCollapsed }) => {
|
||||
const { isDarkMode, toggleTheme } = useTheme();
|
||||
const { user, logout } = useAuth();
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await logout();
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
console.error("Logout error:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const userMenuItems = [
|
||||
{
|
||||
key: 'profile',
|
||||
key: "profile",
|
||||
icon: <UserOutlined />,
|
||||
label: '个人信息',
|
||||
label: "个人信息",
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
key: "logout",
|
||||
icon: <LogoutOutlined />,
|
||||
label: '退出登录',
|
||||
label: "退出登录",
|
||||
onClick: handleLogout,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<AntHeader
|
||||
style={{
|
||||
padding: 0,
|
||||
background: isDarkMode ? '#141414' : '#fff',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
<AntHeader
|
||||
style={{
|
||||
padding: 0,
|
||||
background: isDarkMode ? "#141414" : "#fff",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<MenuTrigger
|
||||
collapsed={collapsed}
|
||||
<MenuTrigger
|
||||
collapsed={collapsed}
|
||||
setCollapsed={setCollapsed}
|
||||
isDarkMode={isDarkMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mr-6">
|
||||
<Switch
|
||||
checked={isDarkMode}
|
||||
onChange={toggleTheme}
|
||||
checkedChildren="🌙"
|
||||
unCheckedChildren="☀️"
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
menu={{ items: userMenuItems }}
|
||||
|
||||
<div className="flex items-center gap-4 mr-10 h-full select-none">
|
||||
<div className="flex items-center justify-center cursor-pointer h-full" onClick={toggleTheme}>
|
||||
{isDarkMode ? (
|
||||
<GoMoon className="text-2xl" />
|
||||
) : (
|
||||
<LuSun className="text-2xl" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Dropdown
|
||||
menu={{ items: userMenuItems }}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
trigger={["click"]}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<UserOutlined />}
|
||||
className="flex items-center"
|
||||
>
|
||||
{user?.email}
|
||||
</Button>
|
||||
<div className="flex gap-2 items-center justify-center cursor-pointer h-full">
|
||||
{user?.user_metadata?.picture ? (
|
||||
<img
|
||||
src={user?.user_metadata?.picture}
|
||||
alt="picture"
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
) : (
|
||||
<div className="text-2xl bg-gray-400 dark:bg-gray-600 rounded-full flex justify-center items-center w-8 h-8 text-white pb-1">
|
||||
{user?.email.slice(0, 1)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="max-w-20 truncate">
|
||||
{!user?.user_metadata?.name || user?.email?.split("@")[0]}
|
||||
</p>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</AntHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
export default Header;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Layout, Menu } from 'antd';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import { getMenuItems } from '@/utils/menuUtils';
|
||||
import { Logo } from '@/components/Layout/Logo';
|
||||
import React, { useMemo } from "react";
|
||||
import { Layout, Menu } from "antd";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { getMenuItems } from "@/utils/menuUtils";
|
||||
import { Logo } from "@/components/Layout/Logo";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
|
||||
const { Sider } = Layout;
|
||||
|
||||
@@ -11,13 +12,18 @@ const Sidebar = ({ collapsed }) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { isDarkMode } = useTheme();
|
||||
const { user } = useAuth();
|
||||
|
||||
const menuItems = useMemo(() => getMenuItems(), []);
|
||||
const menuItems = useMemo(() => {
|
||||
if (!user?.id) return [];
|
||||
const { adminRole: role } = user;
|
||||
return getMenuItems(role);
|
||||
}, [user]);
|
||||
|
||||
const defaultOpenKeys = useMemo(() => {
|
||||
const pathSegments = location.pathname.split('/').filter(Boolean);
|
||||
const pathSegments = location.pathname.split("/").filter(Boolean);
|
||||
return pathSegments.reduce((acc, _, index) => {
|
||||
const path = `/${pathSegments.slice(0, index + 1).join('/')}`;
|
||||
const path = `/${pathSegments.slice(0, index + 1).join("/")}`;
|
||||
acc.push(path);
|
||||
return acc;
|
||||
}, []);
|
||||
@@ -29,18 +35,18 @@ const Sidebar = ({ collapsed }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Sider
|
||||
trigger={null}
|
||||
collapsible
|
||||
<Sider
|
||||
trigger={null}
|
||||
collapsible
|
||||
collapsed={collapsed}
|
||||
theme={isDarkMode ? 'dark' : 'light'}
|
||||
theme={isDarkMode ? "dark" : "light"}
|
||||
width={256}
|
||||
collapsedWidth={80} // 添加这个属性
|
||||
className={`app-sidebar ${collapsed ? 'collapsed' : ''}`} // 添加collapsed类名
|
||||
className={`app-sidebar ${collapsed ? "collapsed" : ""}`} // 添加collapsed类名
|
||||
>
|
||||
<Logo collapsed={collapsed} isDarkMode={isDarkMode} />
|
||||
<Menu
|
||||
theme={isDarkMode ? 'dark' : 'light'}
|
||||
theme={isDarkMode ? "dark" : "light"}
|
||||
mode="inline"
|
||||
selectedKeys={[location.pathname]}
|
||||
defaultOpenKeys={defaultOpenKeys}
|
||||
@@ -51,4 +57,4 @@ const Sidebar = ({ collapsed }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Sidebar);
|
||||
export default React.memo(Sidebar);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Navigate,useLocation } from 'react-router-dom';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { Spin } from 'antd';
|
||||
import React from "react";
|
||||
import { Navigate, useLocation } from "react-router-dom";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { Spin } from "antd";
|
||||
|
||||
export const ProtectedRoute = ({ children }) => {
|
||||
const { user, loading } = useAuth();
|
||||
@@ -14,9 +14,11 @@ export const ProtectedRoute = ({ children }) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return <Navigate to={`/login`} replace />;
|
||||
if (!user?.id) {
|
||||
return (
|
||||
<Navigate to={`/login?redirectTo=${location.pathname || ""}`} replace />
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user