为登录后的页面新增切换项目弹窗
This commit is contained in:
@@ -3,15 +3,25 @@
|
|||||||
import { ColorModeContext } from "@contexts/color-mode";
|
import { ColorModeContext } from "@contexts/color-mode";
|
||||||
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
|
import DarkModeOutlined from "@mui/icons-material/DarkModeOutlined";
|
||||||
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
|
import LightModeOutlined from "@mui/icons-material/LightModeOutlined";
|
||||||
|
import Logout from "@mui/icons-material/Logout";
|
||||||
|
import SwapHoriz from "@mui/icons-material/SwapHoriz";
|
||||||
import AppBar from "@mui/material/AppBar";
|
import AppBar from "@mui/material/AppBar";
|
||||||
import Avatar from "@mui/material/Avatar";
|
import Avatar from "@mui/material/Avatar";
|
||||||
|
import ButtonBase from "@mui/material/ButtonBase";
|
||||||
|
import Divider from "@mui/material/Divider";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
|
import Menu from "@mui/material/Menu";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import Stack from "@mui/material/Stack";
|
import Stack from "@mui/material/Stack";
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import { useGetIdentity } from "@refinedev/core";
|
import { useGetIdentity, useLogout } from "@refinedev/core";
|
||||||
import { HamburgerMenu, RefineThemedLayoutHeaderProps } from "@refinedev/mui";
|
import { HamburgerMenu, RefineThemedLayoutHeaderProps } from "@refinedev/mui";
|
||||||
import React, { useContext } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
|
import { ProjectSelector } from "@components/project/ProjectSelector";
|
||||||
|
import { setMapWorkspace, setNetworkName } from "@config/config";
|
||||||
|
|
||||||
type IUser = {
|
type IUser = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -23,9 +33,35 @@ export const Header: React.FC<RefineThemedLayoutHeaderProps> = ({
|
|||||||
sticky = true,
|
sticky = true,
|
||||||
}) => {
|
}) => {
|
||||||
const { mode, setMode } = useContext(ColorModeContext);
|
const { mode, setMode } = useContext(ColorModeContext);
|
||||||
|
const { mutate: logout } = useLogout();
|
||||||
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||||
|
const [showProjectSelector, setShowProjectSelector] = useState(false);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
|
||||||
const { data: user } = useGetIdentity<IUser>();
|
const { data: user } = useGetIdentity<IUser>();
|
||||||
|
|
||||||
|
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMenuClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSwitchProjectClick = () => {
|
||||||
|
handleMenuClose();
|
||||||
|
setShowProjectSelector(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleProjectSelect = (workspace: string, networkName: string) => {
|
||||||
|
setMapWorkspace(workspace);
|
||||||
|
setNetworkName(networkName);
|
||||||
|
localStorage.setItem("NEXT_PUBLIC_MAP_WORKSPACE", workspace);
|
||||||
|
localStorage.setItem("NEXT_PUBLIC_NETWORK_NAME", networkName);
|
||||||
|
setShowProjectSelector(false);
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position={sticky ? "sticky" : "relative"}>
|
<AppBar position={sticky ? "sticky" : "relative"}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
@@ -52,27 +88,118 @@ export const Header: React.FC<RefineThemedLayoutHeaderProps> = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
{(user?.avatar || user?.name) && (
|
{(user?.avatar || user?.name) && (
|
||||||
<Stack
|
<>
|
||||||
direction="row"
|
<ButtonBase
|
||||||
gap="16px"
|
onClick={handleMenuOpen}
|
||||||
alignItems="center"
|
sx={{
|
||||||
justifyContent="center"
|
borderRadius: "30px",
|
||||||
>
|
padding: "6px 12px",
|
||||||
{user?.name && (
|
marginLeft: "8px",
|
||||||
<Typography
|
transition: "all 0.3s ease",
|
||||||
sx={{
|
border: "1px solid transparent",
|
||||||
display: {
|
"&:hover": {
|
||||||
xs: "none",
|
backgroundColor:
|
||||||
sm: "inline-block",
|
mode === "dark"
|
||||||
},
|
? "rgba(255, 255, 255, 0.05)"
|
||||||
}}
|
: "rgba(0, 0, 0, 0.04)",
|
||||||
variant="subtitle2"
|
transform: "translateY(-1px)",
|
||||||
|
border: `1px solid ${mode === "dark"
|
||||||
|
? "rgba(255, 255, 255, 0.2)"
|
||||||
|
: "rgba(0, 0, 0, 0.1)"
|
||||||
|
}`,
|
||||||
|
boxShadow:
|
||||||
|
mode === "dark"
|
||||||
|
? "0 4px 12px rgba(0,0,0,0.3)"
|
||||||
|
: "0 4px 12px rgba(0,0,0,0.08)",
|
||||||
|
},
|
||||||
|
"&:active": {
|
||||||
|
transform: "translateY(0px)",
|
||||||
|
boxShadow: "none",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
gap="12px"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
>
|
>
|
||||||
{user?.name}
|
{user?.name && (
|
||||||
</Typography>
|
<Typography
|
||||||
)}
|
sx={{
|
||||||
<Avatar src={user?.avatar} alt={user?.name} />
|
display: {
|
||||||
</Stack>
|
xs: "none",
|
||||||
|
sm: "inline-block",
|
||||||
|
},
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
|
variant="subtitle2"
|
||||||
|
>
|
||||||
|
{user?.name}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<Avatar
|
||||||
|
src={user?.avatar}
|
||||||
|
alt={user?.name}
|
||||||
|
sx={{
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
border: `2px solid ${mode === "dark"
|
||||||
|
? "rgba(255,255,255,0.2)"
|
||||||
|
: "rgba(0,0,0,0.1)"
|
||||||
|
}`,
|
||||||
|
transition: "transform 0.3s ease",
|
||||||
|
".MuiButtonBase-root:hover &": {
|
||||||
|
transform: "rotate(5deg) scale(1.05)",
|
||||||
|
borderColor: "primary.main",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</ButtonBase>
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={open}
|
||||||
|
onClose={handleMenuClose}
|
||||||
|
transformOrigin={{ horizontal: "right", vertical: "top" }}
|
||||||
|
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
borderRadius: 2,
|
||||||
|
minWidth: 180,
|
||||||
|
marginTop: "8px",
|
||||||
|
background:
|
||||||
|
mode === "dark"
|
||||||
|
? "rgba(30, 30, 30, 0.95)"
|
||||||
|
: "rgba(255, 255, 255, 0.95)",
|
||||||
|
backdropFilter: "blur(10px)",
|
||||||
|
boxShadow:
|
||||||
|
mode === "dark"
|
||||||
|
? "0px 4px 20px rgba(0, 0, 0, 0.5)"
|
||||||
|
: "0px 4px 20px rgba(0, 0, 0, 0.1)",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={handleSwitchProjectClick}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<SwapHoriz fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>切换项目</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
<Divider />
|
||||||
|
<MenuItem onClick={() => logout()}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Logout fontSize="small" />
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>登出</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
<ProjectSelector
|
||||||
|
open={showProjectSelector}
|
||||||
|
onSelect={handleProjectSelect}
|
||||||
|
onClose={() => setShowProjectSelector(false)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -12,12 +12,15 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
Fade,
|
Fade,
|
||||||
|
IconButton,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
interface ProjectSelectorProps {
|
interface ProjectSelectorProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onSelect: (workspace: string, networkName: string) => void;
|
onSelect: (workspace: string, networkName: string) => void;
|
||||||
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROJECTS = [
|
const PROJECTS = [
|
||||||
@@ -29,6 +32,7 @@ const PROJECTS = [
|
|||||||
export const ProjectSelector: React.FC<ProjectSelectorProps> = ({
|
export const ProjectSelector: React.FC<ProjectSelectorProps> = ({
|
||||||
open,
|
open,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
const [workspace, setWorkspace] = useState(PROJECTS[0].workspace);
|
const [workspace, setWorkspace] = useState(PROJECTS[0].workspace);
|
||||||
const [networkName, setNetworkName] = useState(PROJECTS[0].networkName);
|
const [networkName, setNetworkName] = useState(PROJECTS[0].networkName);
|
||||||
@@ -41,7 +45,8 @@ export const ProjectSelector: React.FC<ProjectSelectorProps> = ({
|
|||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={open}
|
open={open}
|
||||||
disableEscapeKeyDown
|
disableEscapeKeyDown={!onClose}
|
||||||
|
onClose={onClose ? onClose : undefined}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
paper: {
|
paper: {
|
||||||
sx: {
|
sx: {
|
||||||
@@ -50,12 +55,27 @@ export const ProjectSelector: React.FC<ProjectSelectorProps> = ({
|
|||||||
minWidth: 400,
|
minWidth: 400,
|
||||||
background: "rgba(255, 255, 255, 0.95)",
|
background: "rgba(255, 255, 255, 0.95)",
|
||||||
backdropFilter: "blur(10px)",
|
backdropFilter: "blur(10px)",
|
||||||
|
position: "relative",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
slots={{ transition: Fade }}
|
slots={{ transition: Fade }}
|
||||||
transitionDuration={500}
|
transitionDuration={500}
|
||||||
>
|
>
|
||||||
|
{onClose && (
|
||||||
|
<IconButton
|
||||||
|
aria-label="close"
|
||||||
|
onClick={onClose}
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 8,
|
||||||
|
top: 8,
|
||||||
|
color: (theme) => theme.palette.grey[500],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", mb: 2 }}>
|
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "center", mb: 2 }}>
|
||||||
<Box sx={{ transform: "scale(1.5)", mb: 2, mt: 1 }}>
|
<Box sx={{ transform: "scale(1.5)", mb: 2, mt: 1 }}>
|
||||||
<Title />
|
<Title />
|
||||||
|
|||||||
Reference in New Issue
Block a user