feat: 删除无用组件
This commit is contained in:
@@ -1,222 +0,0 @@
|
||||
/*!
|
||||
|
||||
=========================================================
|
||||
* Argon Dashboard Chakra PRO - v1.0.0
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/argon-dashboard-chakra-pro
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com/)
|
||||
|
||||
* Designed and Coded by Simmmple & Creative Tim
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
*/
|
||||
|
||||
// Chakra Imports
|
||||
import {
|
||||
Box,
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
Flex,
|
||||
Link,
|
||||
useColorModeValue,
|
||||
} from "@chakra-ui/react";
|
||||
import { SidebarContext } from "contexts/SidebarContext";
|
||||
import PropTypes from "prop-types";
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import AdminNavbarLinks from "./AdminNavbarLinks";
|
||||
import { HamburgerIcon } from "@chakra-ui/icons";
|
||||
|
||||
export default function AdminNavbar(props) {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
|
||||
const {
|
||||
sidebarWidth,
|
||||
setSidebarWidth,
|
||||
toggleSidebar,
|
||||
setToggleSidebar,
|
||||
} = useContext(SidebarContext);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("scroll", changeNavbar);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", changeNavbar);
|
||||
};
|
||||
});
|
||||
|
||||
const {
|
||||
variant,
|
||||
children,
|
||||
fixed,
|
||||
secondary,
|
||||
brandText,
|
||||
onOpen,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
// Here are all the props that may change depending on navbar's type or state.(secondary, variant, scrolled)
|
||||
let mainText =
|
||||
fixed && scrolled
|
||||
? useColorModeValue("gray.700", "gray.200")
|
||||
: useColorModeValue("white", "gray.200");
|
||||
let secondaryText =
|
||||
fixed && scrolled
|
||||
? useColorModeValue("gray.700", "gray.200")
|
||||
: useColorModeValue("white", "gray.200");
|
||||
let navbarPosition = "absolute";
|
||||
let navbarFilter = "none";
|
||||
let navbarBackdrop = "blur(20px)";
|
||||
let navbarShadow = "none";
|
||||
let navbarBg = "none";
|
||||
let navbarBorder = "transparent";
|
||||
let secondaryMargin = "0px";
|
||||
let paddingX = "15px";
|
||||
if (props.fixed === true)
|
||||
if (scrolled === true) {
|
||||
navbarPosition = "fixed";
|
||||
navbarShadow = useColorModeValue(
|
||||
"0px 7px 23px rgba(0, 0, 0, 0.05)",
|
||||
"none"
|
||||
);
|
||||
navbarBg = useColorModeValue(
|
||||
"linear-gradient(112.83deg, rgba(255, 255, 255, 0.82) 0%, rgba(255, 255, 255, 0.8) 110.84%)",
|
||||
"linear-gradient(112.83deg, rgba(255, 255, 255, 0.21) 0%, rgba(255, 255, 255, 0) 110.84%)"
|
||||
);
|
||||
navbarBorder = useColorModeValue("#FFFFFF", "rgba(255, 255, 255, 0.31)");
|
||||
navbarFilter = useColorModeValue(
|
||||
"none",
|
||||
"drop-shadow(0px 7px 23px rgba(0, 0, 0, 0.05))"
|
||||
);
|
||||
}
|
||||
if (props.secondary) {
|
||||
navbarBackdrop = "none";
|
||||
navbarPosition = "absolute";
|
||||
mainText = "white";
|
||||
secondaryText = "white";
|
||||
secondaryMargin = "22px";
|
||||
paddingX = "30px";
|
||||
}
|
||||
const changeNavbar = () => {
|
||||
if (window.scrollY > 1) {
|
||||
setScrolled(true);
|
||||
} else {
|
||||
setScrolled(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
position={navbarPosition}
|
||||
boxShadow={navbarShadow}
|
||||
bg={navbarBg}
|
||||
borderColor={navbarBorder}
|
||||
filter={navbarFilter}
|
||||
backdropFilter={navbarBackdrop}
|
||||
borderWidth="1.5px"
|
||||
borderStyle="solid"
|
||||
transitionDelay="0s, 0s, 0s, 0s"
|
||||
transitionDuration=" 0.25s, 0.25s, 0.25s, 0s"
|
||||
transition-property="box-shadow, background-color, filter, border"
|
||||
transitionTimingFunction="linear, linear, linear, linear"
|
||||
alignItems={{ xl: "center" }}
|
||||
borderRadius="16px"
|
||||
display="flex"
|
||||
minH="75px"
|
||||
justifyContent={{ xl: "center" }}
|
||||
lineHeight="25.6px"
|
||||
mx="auto"
|
||||
mt={secondaryMargin}
|
||||
pb="8px"
|
||||
left={document.documentElement.dir === "rtl" ? "30px" : ""}
|
||||
right={document.documentElement.dir === "rtl" ? "" : "30px"}
|
||||
px={{
|
||||
sm: paddingX,
|
||||
md: "30px",
|
||||
}}
|
||||
ps={{
|
||||
xl: "12px",
|
||||
}}
|
||||
pt="8px"
|
||||
top="18px"
|
||||
w={{ sm: "calc(100vw - 30px)", xl: "calc(100vw - 75px - 275px)" }}
|
||||
>
|
||||
<Flex
|
||||
w="100%"
|
||||
flexDirection={{
|
||||
sm: "column",
|
||||
md: "row",
|
||||
}}
|
||||
alignItems={{ xl: "center" }}
|
||||
>
|
||||
<Box mb={{ sm: "8px", md: "0px" }}>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem color={mainText}>
|
||||
<BreadcrumbLink href="#" color={secondaryText}>
|
||||
Pages
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem color={mainText}>
|
||||
<BreadcrumbLink href="#" color={mainText}>
|
||||
{brandText}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
{/* Here we create navbar brand, based on route name */}
|
||||
<Link
|
||||
color={mainText}
|
||||
href="#"
|
||||
bg="inherit"
|
||||
borderRadius="inherit"
|
||||
fontWeight="bold"
|
||||
_hover={{ color: { mainText } }}
|
||||
_active={{
|
||||
bg: "inherit",
|
||||
transform: "none",
|
||||
borderColor: "transparent",
|
||||
}}
|
||||
_focus={{
|
||||
boxShadow: "none",
|
||||
}}
|
||||
>
|
||||
{brandText}
|
||||
</Link>
|
||||
</Box>
|
||||
<HamburgerIcon
|
||||
w="100px"
|
||||
h="20px"
|
||||
ms="20px"
|
||||
color="#fff"
|
||||
cursor="pointer"
|
||||
display={{ sm: "none", xl: "block" }}
|
||||
onClick={() => {
|
||||
setSidebarWidth(sidebarWidth === 275 ? 120 : 275);
|
||||
setToggleSidebar(!toggleSidebar);
|
||||
}}
|
||||
/>
|
||||
<Box ms="auto" w={{ sm: "100%", md: "unset" }}>
|
||||
<AdminNavbarLinks
|
||||
onOpen={props.onOpen}
|
||||
logoText={props.logoText}
|
||||
secondary={props.secondary}
|
||||
fixed={props.fixed}
|
||||
scrolled={scrolled}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
AdminNavbar.propTypes = {
|
||||
brandText: PropTypes.string,
|
||||
variant: PropTypes.string,
|
||||
secondary: PropTypes.bool,
|
||||
fixed: PropTypes.bool,
|
||||
onOpen: PropTypes.func,
|
||||
};
|
||||
@@ -1,335 +0,0 @@
|
||||
/*!
|
||||
|
||||
=========================================================
|
||||
* Argon Dashboard Chakra PRO - v1.0.0
|
||||
=========================================================
|
||||
|
||||
* Product Page: https://www.creative-tim.com/product/argon-dashboard-chakra-pro
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com/)
|
||||
|
||||
* Designed and Coded by Simmmple & Creative Tim
|
||||
|
||||
=========================================================
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
*/
|
||||
|
||||
// Chakra Icons
|
||||
import { BellIcon } from "@chakra-ui/icons";
|
||||
// Chakra Imports
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Text,
|
||||
Stack,
|
||||
Box,
|
||||
useColorMode,
|
||||
useColorModeValue,
|
||||
Avatar,
|
||||
HStack,
|
||||
Divider,
|
||||
} from "@chakra-ui/react";
|
||||
// Assets
|
||||
import avatar1 from "assets/img/avatars/avatar1.png";
|
||||
import avatar2 from "assets/img/avatars/avatar2.png";
|
||||
import avatar3 from "assets/img/avatars/avatar3.png";
|
||||
// Custom Icons
|
||||
import { ProfileIcon, SettingsIcon } from "components/Icons/Icons";
|
||||
// Custom Components
|
||||
import { ItemContent } from "components/Menu/ItemContent";
|
||||
import { SearchBar } from "components/Navbars/SearchBar/SearchBar";
|
||||
import { SidebarResponsive } from "components/Sidebar/Sidebar";
|
||||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import routes from "routes.js";
|
||||
import {
|
||||
ArgonLogoDark,
|
||||
ChakraLogoDark,
|
||||
ArgonLogoLight,
|
||||
ChakraLogoLight,
|
||||
} from "components/Icons/Icons";
|
||||
import { useAuth } from "contexts/AuthContext";
|
||||
import SubscriptionBadge from "components/Subscription/SubscriptionBadge";
|
||||
import SubscriptionModal from "components/Subscription/SubscriptionModal";
|
||||
|
||||
export default function HeaderLinks(props) {
|
||||
console.log('🚀 [AdminNavbarLinks] 组件已加载');
|
||||
|
||||
const {
|
||||
variant,
|
||||
children,
|
||||
fixed,
|
||||
scrolled,
|
||||
secondary,
|
||||
onOpen,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const { user, isAuthenticated, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
console.log('👤 [AdminNavbarLinks] 用户状态:', { user, isAuthenticated });
|
||||
|
||||
// 订阅信息状态
|
||||
const [subscriptionInfo, setSubscriptionInfo] = React.useState({
|
||||
type: 'free',
|
||||
status: 'active',
|
||||
days_left: 0,
|
||||
is_active: true
|
||||
});
|
||||
const [isSubscriptionModalOpen, setIsSubscriptionModalOpen] = React.useState(false);
|
||||
|
||||
// 加载订阅信息
|
||||
React.useEffect(() => {
|
||||
console.log('🔍 [AdminNavbarLinks] 订阅徽章 - 认证状态:', isAuthenticated, 'userId:', user?.id);
|
||||
|
||||
if (isAuthenticated && user) {
|
||||
const loadSubscriptionInfo = async () => {
|
||||
try {
|
||||
const base = (process.env.NODE_ENV === 'production' ? '' : process.env.REACT_APP_API_URL || 'http://49.232.185.254:5001');
|
||||
console.log('🌐 [AdminNavbarLinks] 订阅徽章 - API:', base + '/api/subscription/current');
|
||||
const response = await fetch(base + '/api/subscription/current', {
|
||||
credentials: 'include',
|
||||
});
|
||||
console.log('📡 [AdminNavbarLinks] 订阅徽章 - 响应:', response.status);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ [AdminNavbarLinks] 订阅徽章 - 完整响应数据:', data);
|
||||
console.log('🔍 [AdminNavbarLinks] 订阅徽章 - data.data:', data.data);
|
||||
console.log('🔍 [AdminNavbarLinks] 订阅徽章 - type值:', data.data?.type, '类型:', typeof data.data?.type);
|
||||
|
||||
if (data.success && data.data) {
|
||||
// 数据标准化处理:确保type字段是小写的 'free', 'pro', 或 'max'
|
||||
const normalizedData = {
|
||||
type: (data.data.type || data.data.subscription_type || 'free').toLowerCase(),
|
||||
status: data.data.status || 'active',
|
||||
days_left: data.data.days_left || 0,
|
||||
is_active: data.data.is_active !== false,
|
||||
end_date: data.data.end_date || null
|
||||
};
|
||||
console.log('✨ [AdminNavbarLinks] 订阅徽章 - 标准化后:', normalizedData);
|
||||
setSubscriptionInfo(normalizedData);
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ [AdminNavbarLinks] 订阅徽章 - API 失败,使用默认值');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ [AdminNavbarLinks] 订阅徽章 - 错误:', error);
|
||||
}
|
||||
};
|
||||
loadSubscriptionInfo();
|
||||
} else {
|
||||
// 用户未登录时,重置为免费版
|
||||
console.warn('🚫 [AdminNavbarLinks] 订阅徽章 - 用户未登录,重置为免费版');
|
||||
setSubscriptionInfo({
|
||||
type: 'free',
|
||||
status: 'active',
|
||||
days_left: 0,
|
||||
is_active: true
|
||||
});
|
||||
}
|
||||
}, [isAuthenticated, user?.id]); // 只依赖 user.id 而不是整个 user 对象
|
||||
|
||||
// Chakra Color Mode
|
||||
let navbarIcon =
|
||||
fixed && scrolled
|
||||
? useColorModeValue("gray.700", "gray.200")
|
||||
: useColorModeValue("white", "gray.200");
|
||||
let menuBg = useColorModeValue("white", "navy.800");
|
||||
if (secondary) {
|
||||
navbarIcon = "white";
|
||||
}
|
||||
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
navigate("/auth/signin");
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
pe={{ sm: "0px", md: "16px" }}
|
||||
w={{ sm: "100%", md: "auto" }}
|
||||
alignItems="center"
|
||||
flexDirection="row"
|
||||
>
|
||||
<SearchBar me="18px" />
|
||||
|
||||
{/* 订阅状态徽章 - 仅登录用户可见 */}
|
||||
{console.log('🎨 [订阅徽章] 渲染:', { isAuthenticated, subscriptionInfo })}
|
||||
{isAuthenticated && (
|
||||
<>
|
||||
<SubscriptionBadge
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
onClick={() => setIsSubscriptionModalOpen(true)}
|
||||
/>
|
||||
<SubscriptionModal
|
||||
isOpen={isSubscriptionModalOpen}
|
||||
onClose={() => setIsSubscriptionModalOpen(false)}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 用户认证状态 */}
|
||||
{isAuthenticated ? (
|
||||
// 已登录用户 - 显示用户菜单
|
||||
<Menu>
|
||||
<MenuButton>
|
||||
<HStack spacing={2} cursor="pointer">
|
||||
<Avatar
|
||||
size="sm"
|
||||
name={user?.name}
|
||||
src={user?.avatar}
|
||||
bg="blue.500"
|
||||
/>
|
||||
<Text
|
||||
display={{ sm: "none", md: "flex" }}
|
||||
color={navbarIcon}
|
||||
fontSize="sm"
|
||||
fontWeight="medium"
|
||||
>
|
||||
{user?.name || user?.email}
|
||||
</Text>
|
||||
</HStack>
|
||||
</MenuButton>
|
||||
<MenuList p="16px 8px" bg={menuBg}>
|
||||
<Flex flexDirection="column">
|
||||
<MenuItem borderRadius="8px" mb="10px" onClick={() => navigate("/admin/profile")}>
|
||||
<HStack spacing={3}>
|
||||
<Avatar size="sm" name={user?.name} src={user?.avatar} />
|
||||
<Box>
|
||||
<Text fontWeight="bold" fontSize="sm">{user?.name}</Text>
|
||||
<Text fontSize="xs" color="gray.500">{user?.email}</Text>
|
||||
</Box>
|
||||
</HStack>
|
||||
</MenuItem>
|
||||
<Divider my={2} />
|
||||
<MenuItem borderRadius="8px" mb="10px" onClick={() => navigate("/admin/profile")}>
|
||||
<Text>个人资料</Text>
|
||||
</MenuItem>
|
||||
<MenuItem borderRadius="8px" mb="10px" onClick={() => navigate("/admin/settings")}>
|
||||
<Text>设置</Text>
|
||||
</MenuItem>
|
||||
<Divider my={2} />
|
||||
<MenuItem borderRadius="8px" onClick={handleLogout}>
|
||||
<Text color="red.500">退出登录</Text>
|
||||
</MenuItem>
|
||||
</Flex>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
) : (
|
||||
// 未登录用户 - 显示登录按钮
|
||||
<NavLink to="/auth/signin">
|
||||
<Button
|
||||
ms="0px"
|
||||
px="0px"
|
||||
me={{ sm: "2px", md: "16px" }}
|
||||
color={navbarIcon}
|
||||
variant="no-effects"
|
||||
rightIcon={
|
||||
document.documentElement.dir ? (
|
||||
""
|
||||
) : (
|
||||
<ProfileIcon color={navbarIcon} w="22px" h="22px" me="0px" />
|
||||
)
|
||||
}
|
||||
leftIcon={
|
||||
document.documentElement.dir ? (
|
||||
<ProfileIcon color={navbarIcon} w="22px" h="22px" me="0px" />
|
||||
) : (
|
||||
""
|
||||
)
|
||||
}
|
||||
>
|
||||
<Text display={{ sm: "none", md: "flex" }}>登录</Text>
|
||||
</Button>
|
||||
</NavLink>
|
||||
)}
|
||||
|
||||
<SidebarResponsive
|
||||
logo={
|
||||
<Stack direction="row" spacing="12px" align="center" justify="center">
|
||||
{colorMode === "dark" ? (
|
||||
<ArgonLogoLight w="74px" h="27px" />
|
||||
) : (
|
||||
<ArgonLogoDark w="74px" h="27px" />
|
||||
)}
|
||||
<Box
|
||||
w="1px"
|
||||
h="20px"
|
||||
bg={colorMode === "dark" ? "white" : "gray.700"}
|
||||
/>
|
||||
{colorMode === "dark" ? (
|
||||
<ChakraLogoLight w="82px" h="21px" />
|
||||
) : (
|
||||
<ChakraLogoDark w="82px" h="21px" />
|
||||
)}
|
||||
</Stack>
|
||||
}
|
||||
colorMode={colorMode}
|
||||
secondary={props.secondary}
|
||||
routes={routes}
|
||||
{...rest}
|
||||
/>
|
||||
<SettingsIcon
|
||||
cursor="pointer"
|
||||
ms={{ base: "16px", xl: "0px" }}
|
||||
me="16px"
|
||||
onClick={props.onOpen}
|
||||
color={navbarIcon}
|
||||
w="18px"
|
||||
h="18px"
|
||||
/>
|
||||
<Menu>
|
||||
<MenuButton>
|
||||
<BellIcon color={navbarIcon} w="18px" h="18px" />
|
||||
</MenuButton>
|
||||
<MenuList p="16px 8px" bg={menuBg}>
|
||||
<Flex flexDirection="column">
|
||||
<MenuItem borderRadius="8px" mb="10px">
|
||||
<ItemContent
|
||||
time="13 minutes ago"
|
||||
info="from Alicia"
|
||||
boldInfo="New Message"
|
||||
aName="Alicia"
|
||||
aSrc={avatar1}
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem borderRadius="8px" mb="10px">
|
||||
<ItemContent
|
||||
time="2 days ago"
|
||||
info="by Josh Henry"
|
||||
boldInfo="New Album"
|
||||
aName="Josh Henry"
|
||||
aSrc={avatar2}
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem borderRadius="8px">
|
||||
<ItemContent
|
||||
time="3 days ago"
|
||||
info="Payment succesfully completed!"
|
||||
boldInfo=""
|
||||
aName="Kara"
|
||||
aSrc={avatar3}
|
||||
/>
|
||||
</MenuItem>
|
||||
</Flex>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
HeaderLinks.propTypes = {
|
||||
variant: PropTypes.string,
|
||||
fixed: PropTypes.bool,
|
||||
secondary: PropTypes.bool,
|
||||
onOpen: PropTypes.func,
|
||||
};
|
||||
@@ -626,23 +626,15 @@ export default function HomeNavbar() {
|
||||
|
||||
// 加载订阅信息
|
||||
React.useEffect(() => {
|
||||
console.log('🔍 [HomeNavbar] 订阅徽章 - 认证状态:', isAuthenticated, 'userId:', user?.id);
|
||||
|
||||
if (isAuthenticated && user) {
|
||||
const loadSubscriptionInfo = async () => {
|
||||
try {
|
||||
const base = getApiBase();
|
||||
console.log('🌐 [HomeNavbar] 订阅徽章 - API:', base + '/api/subscription/current');
|
||||
const response = await fetch(base + '/api/subscription/current', {
|
||||
credentials: 'include',
|
||||
});
|
||||
console.log('📡 [HomeNavbar] 订阅徽章 - 响应:', response.status);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
console.log('✅ [HomeNavbar] 订阅徽章 - 完整响应数据:', data);
|
||||
console.log('🔍 [HomeNavbar] 订阅徽章 - data.data:', data.data);
|
||||
console.log('🔍 [HomeNavbar] 订阅徽章 - type值:', data.data?.type, '类型:', typeof data.data?.type);
|
||||
|
||||
if (data.success && data.data) {
|
||||
// 数据标准化处理:确保type字段是小写的 'free', 'pro', 或 'max'
|
||||
const normalizedData = {
|
||||
@@ -652,20 +644,16 @@ export default function HomeNavbar() {
|
||||
is_active: data.data.is_active !== false,
|
||||
end_date: data.data.end_date || null
|
||||
};
|
||||
console.log('✨ [HomeNavbar] 订阅徽章 - 标准化后:', normalizedData);
|
||||
setSubscriptionInfo(normalizedData);
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ [HomeNavbar] 订阅徽章 - API 失败,使用默认值');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ [HomeNavbar] 订阅徽章 - 错误:', error);
|
||||
logger.error('HomeNavbar', '加载订阅信息失败', error);
|
||||
}
|
||||
};
|
||||
loadSubscriptionInfo();
|
||||
} else {
|
||||
// 用户未登录时,重置为免费版
|
||||
console.warn('🚫 [HomeNavbar] 订阅徽章 - 用户未登录,重置为免费版');
|
||||
setSubscriptionInfo({
|
||||
type: 'free',
|
||||
status: 'active',
|
||||
@@ -775,7 +763,6 @@ export default function HomeNavbar() {
|
||||
/>
|
||||
|
||||
{/* 订阅状态徽章 - 仅登录用户可见 */}
|
||||
{console.log('🎨 [HomeNavbar] 订阅徽章 - 渲染:', { isAuthenticated, user: !!user, subscriptionInfo })}
|
||||
{isAuthenticated && user && (
|
||||
<>
|
||||
<SubscriptionBadge
|
||||
|
||||
@@ -1,719 +0,0 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/*!
|
||||
=========================================================
|
||||
* Argon Dashboard Chakra PRO - v1.0.0
|
||||
=========================================================
|
||||
* Product Page: https://www.creative-tim.com/product/argon-dashboard-chakra-pro
|
||||
* Copyright 2022 Creative Tim (https://www.creative-tim.com/)
|
||||
* Designed and Coded by Simmmple & Creative Tim
|
||||
=========================================================
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*/
|
||||
|
||||
import { HamburgerIcon } from '@chakra-ui/icons';
|
||||
// chakra imports
|
||||
import {
|
||||
Accordion,
|
||||
AccordionButton,
|
||||
AccordionIcon,
|
||||
AccordionItem,
|
||||
AccordionPanel,
|
||||
Box,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerCloseButton,
|
||||
DrawerContent,
|
||||
DrawerOverlay,
|
||||
Flex,
|
||||
HStack,
|
||||
Icon,
|
||||
List,
|
||||
ListItem,
|
||||
Stack,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import IconBox from 'components/Icons/IconBox';
|
||||
import {
|
||||
renderThumbDark,
|
||||
renderThumbLight,
|
||||
renderTrack,
|
||||
renderTrackRTL,
|
||||
renderView,
|
||||
renderViewRTL
|
||||
} from 'components/Scrollbar/Scrollbar';
|
||||
import { HSeparator } from 'components/Separator/Separator';
|
||||
import { SidebarContext } from 'contexts/SidebarContext';
|
||||
import React from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { FaCircle } from 'react-icons/fa';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
import SidebarDocs from './SidebarDocs';
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
function Sidebar(props) {
|
||||
// to check for active links and opened collapses
|
||||
let location = useLocation();
|
||||
|
||||
const { routes, landing } = props;
|
||||
|
||||
// this is for the rest of the collapses
|
||||
const { sidebarWidth, setSidebarWidth, toggleSidebar } = React.useContext(SidebarContext);
|
||||
|
||||
let variantChange = '0.2s linear';
|
||||
// verifies if routeName is the one active (in browser input)
|
||||
const activeRoute = (routeName) => {
|
||||
return location.pathname.includes(routeName);
|
||||
};
|
||||
// this function creates the links and collapses that appear in the sidebar (left menu)
|
||||
const createLinks = (routes) => {
|
||||
// Chakra Color Mode
|
||||
let activeBg = 'blue.500';
|
||||
let inactiveBg = useColorModeValue('transparent', 'navy.700');
|
||||
let activeColor = useColorModeValue('gray.700', 'white');
|
||||
let inactiveColor = useColorModeValue('gray.400', 'gray.400');
|
||||
let sidebarActiveShadow = '0px 7px 11px rgba(0, 0, 0, 0.04)';
|
||||
let activeAccordionBg = useColorModeValue('white', 'navy.700');
|
||||
let activeColorIcon = 'white';
|
||||
let inactiveColorIcon = 'blue.500';
|
||||
|
||||
if (landing) {
|
||||
activeBg = 'white';
|
||||
inactiveBg = 'transparent';
|
||||
activeColor = 'white';
|
||||
inactiveColor = 'white';
|
||||
sidebarActiveShadow = '0px 7px 11px rgba(0, 0, 0, 0.04)';
|
||||
activeAccordionBg = 'rgba(255, 255, 255, 0.11)';
|
||||
activeColorIcon = 'blue.500';
|
||||
inactiveColorIcon = 'white';
|
||||
}
|
||||
|
||||
return routes.map((prop, key) => {
|
||||
if (prop.category) {
|
||||
return (
|
||||
<Box key={key}>
|
||||
<Text
|
||||
fontSize={sidebarWidth === 275 ? 'md' : 'xs'}
|
||||
color={activeColor}
|
||||
fontWeight='bold'
|
||||
mx='auto'
|
||||
ps={{
|
||||
sm: '10px',
|
||||
xl: '16px'
|
||||
}}
|
||||
pt='18px'
|
||||
pb='12px'
|
||||
key={key}>
|
||||
{prop.name}
|
||||
</Text>
|
||||
{createLinks(prop.items)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
if (prop.collapse) {
|
||||
return (
|
||||
<Accordion key={key} allowToggle>
|
||||
<AccordionItem border='none'>
|
||||
<AccordionButton
|
||||
display='flex'
|
||||
align='center'
|
||||
justify='center'
|
||||
boxShadow={activeRoute(prop.path) && prop.icon ? sidebarActiveShadow : null}
|
||||
_hover={{
|
||||
boxShadow: activeRoute(prop.path) && prop.icon ? sidebarActiveShadow : null
|
||||
}}
|
||||
_focus={{
|
||||
boxShadow: 'none'
|
||||
}}
|
||||
borderRadius='8px'
|
||||
w={{
|
||||
sm: sidebarWidth === 275 ? '100%' : '77%',
|
||||
xl: sidebarWidth === 275 ? '90%' : '70%',
|
||||
'2xl': sidebarWidth === 275 ? '95%' : '77%'
|
||||
}}
|
||||
px={prop.icon ? null : '0px'}
|
||||
py={prop.icon ? '12px' : null}
|
||||
bg={activeRoute(prop.path) && prop.icon ? activeAccordionBg : 'transparent'}
|
||||
ms={sidebarWidth !== 275 ? !prop.icon ? '12px' : '8px' : null}>
|
||||
{activeRoute(prop.path) ? (
|
||||
<Flex
|
||||
fontWeight='bold'
|
||||
boxSize='initial'
|
||||
justifyContent='flex-start'
|
||||
alignItems='center'
|
||||
bg='transparent'
|
||||
transition={variantChange}
|
||||
mx={{
|
||||
xl: 'auto'
|
||||
}}
|
||||
px='0px'
|
||||
borderRadius='8px'
|
||||
w='100%'
|
||||
_hover={{}}
|
||||
_active={{
|
||||
bg: 'inherit',
|
||||
transform: 'none',
|
||||
borderColor: 'transparent',
|
||||
border: 'none'
|
||||
}}
|
||||
_focus={{
|
||||
transform: 'none',
|
||||
borderColor: 'transparent',
|
||||
border: 'none'
|
||||
}}>
|
||||
{prop.icon ? (
|
||||
<Flex justify={sidebarWidth === 275 ? 'flex-start' : 'center'}>
|
||||
<IconBox
|
||||
bg={activeBg}
|
||||
color={activeColorIcon}
|
||||
h='30px'
|
||||
w='30px'
|
||||
me={sidebarWidth === 275 ? '12px' : '0px'}
|
||||
transition={variantChange}>
|
||||
{prop.icon}
|
||||
</IconBox>
|
||||
<Text
|
||||
color={activeColor}
|
||||
my='auto'
|
||||
fontSize='sm'
|
||||
display={sidebarWidth === 275 ? 'block' : 'none'}>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</Flex>
|
||||
) : (
|
||||
<HStack
|
||||
spacing={sidebarWidth === 275 ? '22px' : '0px'}
|
||||
ps={sidebarWidth === 275 ? '10px' : '0px'}
|
||||
ms={sidebarWidth === 275 ? '0px' : '8px'}>
|
||||
<Icon
|
||||
as={FaCircle}
|
||||
w='10px'
|
||||
color='blue.500'
|
||||
display={sidebarWidth === 275 ? 'block' : 'none'}
|
||||
/>
|
||||
<Text color={activeColor} my='auto' fontSize='sm'>
|
||||
{sidebarWidth === 275 ? prop.name : prop.name[0]}
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex
|
||||
fontWeight='bold'
|
||||
boxSize='initial'
|
||||
justifyContent='flex-start'
|
||||
alignItems='center'
|
||||
bg='transparent'
|
||||
mx={{
|
||||
xl: 'auto'
|
||||
}}
|
||||
px='0px'
|
||||
borderRadius='8px'
|
||||
w='100%'
|
||||
_hover={{}}
|
||||
_active={{
|
||||
bg: 'inherit',
|
||||
transform: 'none',
|
||||
borderColor: 'transparent'
|
||||
}}
|
||||
_focus={{
|
||||
borderColor: 'transparent',
|
||||
boxShadow: 'none'
|
||||
}}>
|
||||
{prop.icon ? (
|
||||
<Flex justify={sidebarWidth === 275 ? 'flex-start' : 'center'}>
|
||||
<IconBox
|
||||
bg={inactiveBg}
|
||||
color={inactiveColorIcon}
|
||||
h='30px'
|
||||
w='30px'
|
||||
me={sidebarWidth === 275 ? '12px' : '0px'}
|
||||
transition={variantChange}>
|
||||
{prop.icon}
|
||||
</IconBox>
|
||||
<Text
|
||||
color={inactiveColor}
|
||||
my='auto'
|
||||
fontSize='sm'
|
||||
display={sidebarWidth === 275 ? 'block' : 'none'}>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</Flex>
|
||||
) : (
|
||||
<HStack
|
||||
spacing={sidebarWidth === 275 ? '26px' : '0px'}
|
||||
ps={sidebarWidth === 275 ? '10px' : '0px'}
|
||||
ms={sidebarWidth === 275 ? '0px' : '8px'}>
|
||||
<Icon
|
||||
as={FaCircle}
|
||||
w='6px'
|
||||
color={landing ? 'white' : 'blue.500'}
|
||||
display={sidebarWidth === 275 ? 'block' : 'none'}
|
||||
/>
|
||||
<Text color={inactiveColor} my='auto' fontSize='md' fontWeight='normal'>
|
||||
{sidebarWidth === 275 ? prop.name : prop.name[0]}
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
<AccordionIcon
|
||||
color={landing ? 'white' : 'gray.400'}
|
||||
display={
|
||||
prop.icon ? sidebarWidth === 275 ? (
|
||||
'block'
|
||||
) : (
|
||||
'none'
|
||||
) : sidebarWidth === 275 ? (
|
||||
'block'
|
||||
) : (
|
||||
'none'
|
||||
)
|
||||
}
|
||||
transform={prop.icon ? null : sidebarWidth === 275 ? null : 'translateX(-70%)'}
|
||||
/>
|
||||
</AccordionButton>
|
||||
<AccordionPanel
|
||||
pe={prop.icon ? null : '0px'}
|
||||
pb='8px'
|
||||
ps={prop.icon ? null : sidebarWidth === 275 ? null : '8px'}>
|
||||
<List>
|
||||
{prop.icon ? (
|
||||
createLinks(prop.items) // for bullet accordion links
|
||||
) : (
|
||||
createAccordionLinks(prop.items)
|
||||
) // for non-bullet accordion links
|
||||
}
|
||||
</List>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<NavLink key={key} to={prop.layout + prop.path}>
|
||||
{prop.icon ? (
|
||||
<Box>
|
||||
<HStack spacing='14px' py='15px' px='15px'>
|
||||
<IconBox bg='blue.500' color='white' h='30px' w='30px' transition={variantChange}>
|
||||
{prop.icon}
|
||||
</IconBox>
|
||||
<Text
|
||||
color={activeRoute(prop.path.toLowerCase()) ? activeColor : inactiveColor}
|
||||
fontWeight={activeRoute(prop.name) ? 'bold' : 'normal'}
|
||||
fontSize='sm'>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
) : (
|
||||
<ListItem key={key} ms={sidebarWidth === 275 ? null : '10px'}>
|
||||
<HStack
|
||||
spacing={
|
||||
sidebarWidth === 275 ? activeRoute(prop.path.toLowerCase()) ? (
|
||||
'22px'
|
||||
) : (
|
||||
'26px'
|
||||
) : (
|
||||
'8px'
|
||||
)
|
||||
}
|
||||
py='5px'
|
||||
px={sidebarWidth === 275 ? '10px' : '0px'}>
|
||||
<Icon
|
||||
as={FaCircle}
|
||||
w={activeRoute(prop.path.toLowerCase()) ? '10px' : '6px'}
|
||||
color={landing ? 'white' : 'blue.500'}
|
||||
display={sidebarWidth === 275 ? 'block' : 'none'}
|
||||
/>
|
||||
<Text
|
||||
color={activeRoute(prop.path.toLowerCase()) ? activeColor : inactiveColor}
|
||||
fontWeight={activeRoute(prop.path.toLowerCase()) ? 'bold' : 'normal'}>
|
||||
{sidebarWidth === 275 ? prop.name : prop.name[0]}
|
||||
</Text>
|
||||
</HStack>
|
||||
</ListItem>
|
||||
)}
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const createAccordionLinks = (routes) => {
|
||||
let inactiveColor = useColorModeValue('gray.400', 'gray.400');
|
||||
let activeColor = useColorModeValue('gray.700', 'white');
|
||||
|
||||
if (landing) {
|
||||
inactiveColor = 'white';
|
||||
activeColor = 'white';
|
||||
}
|
||||
|
||||
return routes.map((prop, key) => {
|
||||
return (
|
||||
<NavLink key={key} to={prop.layout + prop.path}>
|
||||
<ListItem key={key} pt='5px' ms={sidebarWidth === 275 ? '26px' : '12px'}>
|
||||
<Text
|
||||
mb='4px'
|
||||
color={activeRoute(prop.path.toLowerCase()) ? activeColor : inactiveColor}
|
||||
fontWeight={activeRoute(prop.path.toLowerCase()) ? 'bold' : 'normal'}
|
||||
fontSize='sm'>
|
||||
{sidebarWidth === 275 ? prop.name : prop.name[0]}
|
||||
</Text>
|
||||
</ListItem>
|
||||
</NavLink>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
let isWindows = navigator.platform.startsWith('Win');
|
||||
let links = <Box>{createLinks(routes)}</Box>;
|
||||
// BRAND
|
||||
// Chakra Color Mode
|
||||
let sidebarBg = useColorModeValue('white', 'navy.800');
|
||||
let sidebarRadius = '20px';
|
||||
let sidebarMargins = '0px';
|
||||
var brand = (
|
||||
<Flex align='center' direction='column' pt={'25px'}>
|
||||
{props.logo}
|
||||
<HSeparator my='20px' />
|
||||
</Flex>
|
||||
);
|
||||
|
||||
let sidebarContent = (
|
||||
<Box>
|
||||
<Box>{brand}</Box>
|
||||
<Stack direction='column' mb='40px'>
|
||||
<Box>{links}</Box>
|
||||
</Stack>
|
||||
<SidebarDocs landing={landing} />
|
||||
</Box>
|
||||
);
|
||||
|
||||
// SIDEBAR
|
||||
return (
|
||||
<Box
|
||||
onMouseEnter={toggleSidebar ? () => setSidebarWidth(sidebarWidth === 120 ? 275 : 120) : null}
|
||||
onMouseLeave={toggleSidebar ? () => setSidebarWidth(sidebarWidth === 275 ? 120 : 275) : null}>
|
||||
<Box display={{ sm: 'none', xl: 'block' }} position='fixed'>
|
||||
<Box
|
||||
bg={landing ? 'transparent' : sidebarBg}
|
||||
transition={variantChange}
|
||||
w={`${sidebarWidth}px`}
|
||||
ms={{
|
||||
sm: '16px'
|
||||
}}
|
||||
my={{
|
||||
sm: '16px'
|
||||
}}
|
||||
h='calc(100vh - 32px)'
|
||||
ps='20px'
|
||||
pe='20px'
|
||||
m={sidebarMargins}
|
||||
borderRadius={sidebarRadius}>
|
||||
<Scrollbars
|
||||
autoHide
|
||||
renderTrackVertical={document.documentElement.dir === 'rtl' ? renderTrackRTL : renderTrack}
|
||||
renderThumbVertical={useColorModeValue(renderThumbLight, renderThumbDark)}
|
||||
renderView={document.documentElement.dir === 'rtl' ? renderViewRTL : renderView}>
|
||||
{sidebarContent}
|
||||
</Scrollbars>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
export function SidebarResponsive(props) {
|
||||
// to check for active links and opened collapses
|
||||
let location = useLocation();
|
||||
|
||||
let variantChange = '0.2s linear';
|
||||
// verifies if routeName is the one active (in browser input)
|
||||
const activeRoute = (routeName) => {
|
||||
return location.pathname.includes(routeName);
|
||||
};
|
||||
|
||||
// Chakra Color Mode
|
||||
let activeBg = 'blue.500';
|
||||
let inactiveBg = useColorModeValue('transparent', 'navy.700');
|
||||
let activeColor = useColorModeValue('gray.700', 'white');
|
||||
let inactiveColor = useColorModeValue('gray.400', 'gray.400');
|
||||
let activeAccordionBg = useColorModeValue('white', 'navy.700');
|
||||
let sidebarActiveShadow = useColorModeValue('0px 7px 11px rgba(0, 0, 0, 0.04)', 'none');
|
||||
let activeColorIcon = 'white';
|
||||
let inactiveColorIcon = 'blue.500';
|
||||
let sidebarBackgroundColor = useColorModeValue('white', 'navy.900');
|
||||
|
||||
// this function creates the links and collapses that appear in the sidebar (left menu)
|
||||
const createLinks = (routes) => {
|
||||
return routes.map((prop, key) => {
|
||||
if (prop.category) {
|
||||
return (
|
||||
<Box key={key}>
|
||||
<Text
|
||||
fontSize={'md'}
|
||||
color={activeColor}
|
||||
fontWeight='bold'
|
||||
mx='auto'
|
||||
ps={{
|
||||
sm: '10px',
|
||||
xl: '16px'
|
||||
}}
|
||||
py='12px'
|
||||
key={key}>
|
||||
{prop.name}
|
||||
</Text>
|
||||
{createLinks(prop.items)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
if (prop.collapse) {
|
||||
return (
|
||||
<Accordion key={key} allowToggle>
|
||||
<AccordionItem border='none'>
|
||||
<AccordionButton
|
||||
as='div'
|
||||
display='flex'
|
||||
align='center'
|
||||
justify='center'
|
||||
key={key}
|
||||
borderRadius='8px'
|
||||
px={prop.icon ? null : '0px'}
|
||||
py={prop.icon ? '12px' : null}
|
||||
boxShadow={activeRoute(prop.path) && prop.icon ? sidebarActiveShadow : 'none'}
|
||||
bg={activeRoute(prop.path) && prop.icon ? activeAccordionBg : 'transparent'}>
|
||||
{activeRoute(prop.path) ? (
|
||||
<Flex
|
||||
fontWeight='bold'
|
||||
boxSize='initial'
|
||||
justifyContent='flex-start'
|
||||
alignItems='center'
|
||||
bg='transparent'
|
||||
transition={variantChange}
|
||||
mx={{
|
||||
xl: 'auto'
|
||||
}}
|
||||
px='0px'
|
||||
borderRadius='8px'
|
||||
_hover={{}}
|
||||
w='100%'
|
||||
_active={{
|
||||
bg: 'inherit',
|
||||
transform: 'none',
|
||||
borderColor: 'transparent'
|
||||
}}>
|
||||
{prop.icon ? (
|
||||
<Flex>
|
||||
<IconBox
|
||||
bg={activeBg}
|
||||
color={activeColorIcon}
|
||||
h='30px'
|
||||
w='30px'
|
||||
me='12px'
|
||||
transition={variantChange}>
|
||||
{prop.icon}
|
||||
</IconBox>
|
||||
<Text color={activeColor} my='auto' fontSize='sm' display={'block'}>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</Flex>
|
||||
) : (
|
||||
<HStack spacing={'22px'} ps='10px' ms='0px'>
|
||||
<Icon as={FaCircle} w='10px' color='blue.500' />
|
||||
<Text as='span' color={activeColor} my='auto' fontSize='sm'>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
</Flex>
|
||||
) : (
|
||||
<Text
|
||||
as='span'
|
||||
fontWeight='bold'
|
||||
boxSize='initial'
|
||||
justifyContent='flex-start'
|
||||
alignItems='center'
|
||||
bg='transparent'
|
||||
mx={{
|
||||
xl: 'auto'
|
||||
}}
|
||||
px='0px'
|
||||
borderRadius='8px'
|
||||
_hover={{}}
|
||||
w='100%'
|
||||
_active={{
|
||||
bg: 'inherit',
|
||||
transform: 'none',
|
||||
borderColor: 'transparent'
|
||||
}}
|
||||
_focus={{
|
||||
boxShadow: 'none'
|
||||
}}>
|
||||
{prop.icon ? (
|
||||
<Flex>
|
||||
<IconBox
|
||||
bg={inactiveBg}
|
||||
color={inactiveColorIcon}
|
||||
h='30px'
|
||||
w='30px'
|
||||
me='12px'
|
||||
transition={variantChange}>
|
||||
{prop.icon}
|
||||
</IconBox>
|
||||
<Text color={inactiveColor} my='auto' fontSize='sm'>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</Flex>
|
||||
) : (
|
||||
<HStack spacing={'26px'} ps={'10px'} ms={'0px'}>
|
||||
<Icon as={FaCircle} w='6px' color='blue.500' />
|
||||
<Text color={inactiveColor} my='auto' fontSize='sm' fontWeight='normal'>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</HStack>
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
<AccordionIcon color='gray.400' />
|
||||
</AccordionButton>
|
||||
<AccordionPanel pe={prop.icon ? null : '0px'} pb='8px'>
|
||||
<List>
|
||||
{prop.icon ? (
|
||||
createLinks(prop.items) // for bullet accordion links
|
||||
) : (
|
||||
createAccordionLinks(prop.items)
|
||||
) // for non-bullet accordion links
|
||||
}
|
||||
</List>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<NavLink key={key} to={prop.layout + prop.path}>
|
||||
{prop.icon ? (
|
||||
<Box>
|
||||
<HStack spacing='14px' py='15px' px='15px'>
|
||||
<IconBox bg='blue.500' color='white' h='30px' w='30px' transition={variantChange}>
|
||||
{prop.icon}
|
||||
</IconBox>
|
||||
<Text
|
||||
color={activeRoute(prop.path.toLowerCase()) ? activeColor : inactiveColor}
|
||||
fontWeight={activeRoute(prop.name) ? 'bold' : 'normal'}
|
||||
fontSize='sm'>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
) : (
|
||||
<ListItem>
|
||||
<HStack spacing='22px' py='5px' px='10px'>
|
||||
<Icon
|
||||
as={FaCircle}
|
||||
w={activeRoute(prop.path.toLowerCase()) ? '10px' : '6px'}
|
||||
color='blue.500'
|
||||
/>
|
||||
<Text
|
||||
color={activeRoute(prop.path.toLowerCase()) ? activeColor : inactiveColor}
|
||||
fontSize='sm'
|
||||
fontWeight={activeRoute(prop.path.toLowerCase()) ? 'bold' : 'normal'}>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</HStack>
|
||||
</ListItem>
|
||||
)}
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const createAccordionLinks = (routes) => {
|
||||
return routes.map((prop, key) => {
|
||||
return (
|
||||
<NavLink key={key} to={prop.layout + prop.path}>
|
||||
<ListItem pt='5px' ms='26px' key={key}>
|
||||
<Text
|
||||
color={activeRoute(prop.path.toLowerCase()) ? activeColor : inactiveColor}
|
||||
fontWeight={activeRoute(prop.path.toLowerCase()) ? 'bold' : 'normal'}
|
||||
fontSize='sm'>
|
||||
{prop.name}
|
||||
</Text>
|
||||
</ListItem>
|
||||
</NavLink>
|
||||
);
|
||||
});
|
||||
};
|
||||
const { logo, display, routes } = props;
|
||||
|
||||
let links = <Box>{createLinks(routes)}</Box>;
|
||||
// BRAND
|
||||
// Chakra Color Mode
|
||||
let hamburgerColor = 'white';
|
||||
|
||||
var brand = (
|
||||
<Box pt={'25px'} mb='12px'>
|
||||
{logo}
|
||||
<HSeparator my='26px' />
|
||||
</Box>
|
||||
);
|
||||
|
||||
// SIDEBAR
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const btnRef = React.useRef();
|
||||
// Color variables
|
||||
return (
|
||||
<Box display={display}>
|
||||
<Box display={{ sm: 'flex', xl: 'none' }} ms='8px'>
|
||||
<HamburgerIcon
|
||||
color={hamburgerColor}
|
||||
w='18px'
|
||||
h='18px'
|
||||
me='16px'
|
||||
ref={btnRef}
|
||||
cursor='pointer'
|
||||
onClick={onOpen}
|
||||
/>
|
||||
<Drawer
|
||||
placement={document.documentElement.dir === 'rtl' ? 'right' : 'left'}
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
finalFocusRef={btnRef}>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent
|
||||
w='250px'
|
||||
bg={sidebarBackgroundColor}
|
||||
maxW='250px'
|
||||
ms={{
|
||||
sm: '16px'
|
||||
}}
|
||||
my={{
|
||||
sm: '16px'
|
||||
}}
|
||||
borderRadius='16px'>
|
||||
<DrawerCloseButton _focus={{ boxShadow: 'none' }} _hover={{ boxShadow: 'none' }} />
|
||||
<DrawerBody maxW='250px' px='1rem'>
|
||||
<Box maxW='100%' h='100vh'>
|
||||
<Box mb='20px'>{brand}</Box>
|
||||
<Stack direction='column' mb='40px'>
|
||||
<Box>{links}</Box>
|
||||
</Stack>
|
||||
<SidebarDocs />
|
||||
</Box>
|
||||
</DrawerBody>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
// PROPS
|
||||
|
||||
export default Sidebar;
|
||||
@@ -1,45 +0,0 @@
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
Image,
|
||||
Link,
|
||||
Stack,
|
||||
Text,
|
||||
useColorModeValue,
|
||||
} from "@chakra-ui/react";
|
||||
import SidebarHelpImage from "assets/img/SidebarHelpImage.png";
|
||||
import { SidebarContext } from "contexts/SidebarContext";
|
||||
import React, { useContext } from "react";
|
||||
|
||||
export default function SidebarDocs({ landing }) {
|
||||
|
||||
const textColor = useColorModeValue("gray.700", "white");
|
||||
|
||||
const { sidebarWidth } = useContext(SidebarContext);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
justify='center'
|
||||
direction='column'
|
||||
align='center'
|
||||
|
||||
|
||||
display={sidebarWidth !== 275 && "none"}
|
||||
>
|
||||
<Image src={SidebarHelpImage} w='165px' ms="24px" />
|
||||
<Flex direction='column' align="center" textAlign='center' mb="12px" me="24px">
|
||||
<Text fontSize='14px' color={landing ? "white" : textColor} fontWeight='bold'>
|
||||
Need help?
|
||||
</Text>
|
||||
<Text fontSize='12px' color={landing ? "white" : 'gray.500'}>
|
||||
Please check our docs.
|
||||
</Text>
|
||||
</Flex>
|
||||
<Link href='#' >
|
||||
<Button variant={landing ? "light" : 'primary'} mb={{ sm: "12px", xl: "16px" }} color={landing && "blue.500"} fontWeight="bold" minW="185px" ms="24px">
|
||||
DOCUMENTATION
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -4,16 +4,9 @@ import { Box, Text, Tooltip, useColorModeValue } from '@chakra-ui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function SubscriptionBadge({ subscriptionInfo, onClick }) {
|
||||
// 🔍 调试:输出接收到的 props
|
||||
console.log('🎯 [SubscriptionBadge] 接收到的 subscriptionInfo:', subscriptionInfo);
|
||||
console.log('🎯 [SubscriptionBadge] subscriptionInfo.type:', subscriptionInfo?.type, '类型:', typeof subscriptionInfo?.type);
|
||||
|
||||
// 根据订阅类型返回样式配置
|
||||
const getBadgeStyles = () => {
|
||||
console.log('🔧 [SubscriptionBadge] getBadgeStyles 执行, type:', subscriptionInfo.type);
|
||||
|
||||
if (subscriptionInfo.type === 'max') {
|
||||
console.log('✅ [SubscriptionBadge] 匹配到 MAX');
|
||||
return {
|
||||
bg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||
color: 'white',
|
||||
@@ -24,7 +17,6 @@ export default function SubscriptionBadge({ subscriptionInfo, onClick }) {
|
||||
};
|
||||
}
|
||||
if (subscriptionInfo.type === 'pro') {
|
||||
console.log('✅ [SubscriptionBadge] 匹配到 PRO');
|
||||
return {
|
||||
bg: 'linear-gradient(135deg, #667eea 0%, #3182CE 100%)',
|
||||
color: 'white',
|
||||
@@ -35,7 +27,6 @@ export default function SubscriptionBadge({ subscriptionInfo, onClick }) {
|
||||
};
|
||||
}
|
||||
// 基础版
|
||||
console.log('⚠️ [SubscriptionBadge] 使用默认基础版');
|
||||
return {
|
||||
bg: 'transparent',
|
||||
color: useColorModeValue('gray.600', 'gray.400'),
|
||||
@@ -49,7 +40,6 @@ export default function SubscriptionBadge({ subscriptionInfo, onClick }) {
|
||||
};
|
||||
|
||||
const styles = getBadgeStyles();
|
||||
console.log('🎨 [SubscriptionBadge] styles 对象:', styles);
|
||||
|
||||
// 智能动态 Tooltip 文本
|
||||
const getTooltipText = () => {
|
||||
@@ -118,10 +108,7 @@ export default function SubscriptionBadge({ subscriptionInfo, onClick }) {
|
||||
}}
|
||||
>
|
||||
{styles.icon && <span style={{ marginRight: '4px' }}>{styles.icon}</span>}
|
||||
{(() => {
|
||||
console.log('📝 [SubscriptionBadge] 渲染文本:', styles.label);
|
||||
return styles.label;
|
||||
})()}
|
||||
{styles.label}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user