Initial commit

This commit is contained in:
2025-10-11 11:55:25 +08:00
parent 467dad8449
commit 8107dee8d3
2879 changed files with 610575 additions and 0 deletions

209
src/layouts/Admin.js Normal file
View File

@@ -0,0 +1,209 @@
/*!
=========================================================
* 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 { Portal, Box, useColorMode, Stack, useDisclosure, useColorModeValue } from '@chakra-ui/react';
import Configurator from 'components/Configurator/Configurator';
import FixedPlugin from 'components/FixedPlugin/FixedPlugin';
import Footer from 'components/Footer/Footer.js';
// Custom components
import MainPanel from 'components/Layout/MainPanel';
import PanelContainer from 'components/Layout/PanelContainer';
import PanelContent from 'components/Layout/PanelContent';
// Layout components
import AdminNavbar from 'components/Navbars/AdminNavbar.js';
import Sidebar from 'components/Sidebar/Sidebar.js';
import { SidebarContext } from 'contexts/SidebarContext';
import React, { useState } from 'react';
import 'react-quill/dist/quill.snow.css'; // ES6
import { Route, Routes, Navigate } from "react-router-dom";
import routes from 'routes.js';
import {
ArgonLogoDark,
ArgonLogoLight,
ChakraLogoDark,
ChakraLogoLight,
ArgonLogoMinifiedDark,
ArgonLogoMinifiedLight
} from 'components/Icons/Icons';
// Custom Chakra theme
export default function Dashboard(props) {
const { ...rest } = props;
// states and functions
const [ fixed, setFixed ] = useState(false);
const [ toggleSidebar, setToggleSidebar ] = useState(false);
const [ sidebarWidth, setSidebarWidth ] = useState(275);
// functions for changing the states from components
const getRoute = () => {
return window.location.pathname !== '/admin/full-screen-maps';
};
const getActiveRoute = (routes) => {
let activeRoute = 'Default Brand Text';
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveRoute = getActiveRoute(routes[i].items);
if (collapseActiveRoute !== activeRoute) {
return collapseActiveRoute;
}
} else if (routes[i].category) {
let categoryActiveRoute = getActiveRoute(routes[i].items);
if (categoryActiveRoute !== activeRoute) {
return categoryActiveRoute;
}
} else {
if (window.location.href.indexOf(routes[i].layout + routes[i].path) !== -1) {
return routes[i].name;
}
}
}
return activeRoute;
};
const getActiveNavbar = (routes) => {
let activeNavbar = false;
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveNavbar = getActiveNavbar(routes[i].items);
if (collapseActiveNavbar !== activeNavbar) {
return collapseActiveNavbar;
}
} else if (routes[i].category) {
let categoryActiveNavbar = getActiveNavbar(routes[i].items);
if (categoryActiveNavbar !== activeNavbar) {
return categoryActiveNavbar;
}
} else {
if (window.location.href.indexOf(routes[i].layout + routes[i].path) !== -1) {
return routes[i].secondaryNavbar;
}
}
}
return activeNavbar;
};
const getRoutes = (routes) => {
return routes.map((route, key) => {
if (route.layout === '/admin') {
return <Route path={route.path} element={route.component} key={key} />
}
if (route.collapse) {
return getRoutes(route.items);
}
if (route.category) {
return getRoutes(route.items);
} else {
return null;
}
});
};
let bgBoxHeight = '40vh';
let bgBoxColor = useColorModeValue('blue.500', 'navy.900');
const { isOpen, onOpen, onClose } = useDisclosure();
const { colorMode } = useColorMode();
document.documentElement.dir = 'ltr';
document.documentElement.layout = 'admin';
// Chakra Color Mode
return (
<Box>
<SidebarContext.Provider
value={{
sidebarWidth,
setSidebarWidth,
toggleSidebar,
setToggleSidebar
}}>
<Box minH={bgBoxHeight} h='100% !important' w='100%' position='absolute' bg={bgBoxColor} top='0' />
<Sidebar
routes={routes}
logo={
sidebarWidth === 275 ? (
<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 === 'light' ? (
<ArgonLogoMinifiedDark w='36px' h='36px' />
) : (
<ArgonLogoMinifiedLight w='36px' h='36px' />
)
}
display='none'
{...rest}
/>
<MainPanel
w={{
base: '100%',
xl: `calc(100% - ${sidebarWidth}px)`
}}>
<Portal>
<Box>
<AdminNavbar
onOpen={onOpen}
logoText={'Argon Dashboard Chakra PRO'}
brandText={getActiveRoute(routes)}
secondary={getActiveNavbar(routes)}
fixed={fixed}
{...rest}
/>
</Box>
</Portal>
{getRoute() ? (
<PanelContent>
<PanelContainer>
<Routes>
{getRoutes(routes)}
<Route
path="/"
element={<Navigate to="/admin/dashboard/default" replace />}
/>
</Routes>
</PanelContainer>
</PanelContent>
) : null}
<Box>
<Footer />
</Box>
<Portal>
<Box>
<FixedPlugin fixed={fixed} onOpen={onOpen} />
</Box>
</Portal>
<Configurator
secondary={getActiveNavbar(routes)}
isOpen={isOpen}
onClose={onClose}
isChecked={fixed}
onSwitch={(value) => {
setFixed(value);
}}
/>
</MainPanel>
</SidebarContext.Provider>
</Box>
);
}

64
src/layouts/Auth.js Normal file
View File

@@ -0,0 +1,64 @@
// src/layouts/Auth.js
import React from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import { Box } from '@chakra-ui/react';
import { useAuth } from '../contexts/AuthContext';
// 导入认证相关页面
import SignInIllustration from '../views/Authentication/SignIn/SignInIllustration';
import SignUpIllustration from '../views/Authentication/SignUp/SignUpIllustration';
// 认证路由组件 - 已登录用户不能访问登录页
const AuthRoute = ({ children }) => {
const { isAuthenticated, isLoading } = useAuth();
// 加载中不做跳转
if (isLoading) {
return children;
}
// 已登录用户跳转到首页
if (isAuthenticated) {
// 检查是否有记录的重定向路径
const redirectPath = localStorage.getItem('redirectPath');
if (redirectPath && redirectPath !== '/auth/signin' && redirectPath !== '/auth/sign-up') {
localStorage.removeItem('redirectPath');
return <Navigate to={redirectPath} replace />;
}
return <Navigate to="/home" replace />;
}
return children;
};
export default function Auth() {
return (
<Box minH="100vh">
<Routes>
{/* 登录页面 */}
<Route
path="/signin"
element={
<AuthRoute>
<SignInIllustration />
</AuthRoute>
}
/>
{/* 注册页面 */}
<Route
path="/sign-up"
element={
<AuthRoute>
<SignUpIllustration />
</AuthRoute>
}
/>
{/* 默认重定向到登录页 */}
<Route path="/" element={<Navigate to="/auth/signin" replace />} />
<Route path="*" element={<Navigate to="/auth/signin" replace />} />
</Routes>
</Box>
);
}

64
src/layouts/AuthBasic.js Normal file
View File

@@ -0,0 +1,64 @@
import PropTypes from 'prop-types';
import React from 'react'; // Chakra imports
import { Box, Flex, Text } from '@chakra-ui/react';
function AuthBasic(props) {
const { children, title, description, image, ...rest } = props;
return (
<Flex
direction='column'
alignSelf='center'
justifySelf='center'
overflow='hidden'
>
<Box
position='absolute'
minH={{ base: '70vh', md: '50vh' }}
maxH={{ base: '70vh', md: '50vh' }}
w={{ md: 'calc(100vw - 50px)' }}
maxW={{ md: 'calc(100vw - 50px)' }}
left='0'
right='0'
bgRepeat='no-repeat'
overflow='hidden'
zIndex='-1'
top='0'
bgImage={image}
bgSize='cover'
mx={{ md: 'auto' }}
mt={{ md: '14px' }}
borderRadius={{ base: '0px', md: '20px' }}
>
<Box w='100vw' h='100vh' bg='blue.500' opacity='0.8'></Box>
</Box>
{title && description ? (
<Flex
direction='column'
textAlign='center'
justifyContent='center'
align='center'
mt='125px'
mb='30px'
>
<Text fontSize='4xl' color='white' fontWeight='bold'>
{title}
</Text>
<Text
fontSize='md'
color='white'
fontWeight='normal'
mt='10px'
mb='26px'
w={{ base: '90%', sm: '60%', lg: '40%', xl: '333px' }}
>
{description}
</Text>
</Flex>
) : null}
{children}
</Flex>
);
}
// PROPS
export default AuthBasic;

42
src/layouts/AuthCover.js Normal file
View File

@@ -0,0 +1,42 @@
import React from "react";
import PropTypes from "prop-types";
// Chakra imports
import { Box, Flex } from "@chakra-ui/react";
function AuthCover(props) {
const { children, image, ...rest } = props;
return (
<Flex position='relative' mb='40px'>
<Flex
minH={{ md: "950px" }}
h={{ sm: "initial", md: "75vh", lg: "85vh" }}
w='100%'
maxW='1044px'
mx='auto'
justifyContent='space-between'
mb='30px'>
{children}
<Box
overflowX='hidden'
h={{ base: "100%", lg: "100%" }}
w='100%'
left='0px'
position='absolute'
bgImage={image}>
<Box
w='100%'
h='100%'
bgSize='cover'
bg='blue.500'
opacity='0.8'></Box>
</Box>
</Flex>
</Flex>
);
}
// PROPS
AuthCover.propTypes = {
image: PropTypes.any,
};
export default AuthCover;

View File

@@ -0,0 +1,75 @@
/*!
=========================================================
* 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 React from "react";
import PropTypes from "prop-types";
// Chakra imports
import { Box, Flex, Image } from "@chakra-ui/react";
function AuthIllustration(props) {
const { children, illustrationBackground, image, ...rest } = props;
// Chakra color mode
return (
<Flex position="relative" mb="70px">
<Flex
h={{ sm: "initial", md: "75vh", lg: "85vh" }}
w="100%"
maxW="1044px"
mx="auto"
justifyContent="space-between"
mb="40px"
pt={{ sm: "100px", md: "0px" }}
>
{children}
<Box
display={{ base: "none", md: "block" }}
overflowX="hidden"
h="100%"
w={{ lg: "50vw", "2xl": "50vw" }}
position="absolute"
right="0px"
>
<Flex
bg={illustrationBackground}
justify="center"
align="end"
w="100%"
h="100%"
bgSize="cover"
bgPosition="50%"
position="absolute"
borderBottomLeftRadius="20px"
>
<Image
boxSize={{ lg: "500px", xl: "600px", "2xl": "790px" }}
src={image}
alt="illustration"
/>
</Flex>
</Box>
</Flex>
</Flex>
);
}
// PROPS
AuthIllustration.propTypes = {
illustrationBackground: PropTypes.string,
image: PropTypes.any,
};
export default AuthIllustration;

86
src/layouts/Home.js Normal file
View File

@@ -0,0 +1,86 @@
// src/layouts/Home.js
import React from "react";
import { Routes, Route } from "react-router-dom";
import { Box } from '@chakra-ui/react';
// 导入导航栏组件 - 使用我们之前修复的版本
import HomeNavbar from "../components/Navbars/HomeNavbar";
// 导入页面组件
import HomePage from "views/Home/HomePage";
import ProfilePage from "views/Profile/ProfilePage";
import SettingsPage from "views/Settings/SettingsPage";
import CenterDashboard from "views/Dashboard/Center";
import Subscription from "views/Pages/Account/Subscription";
import TradingSimulation from "views/TradingSimulation";
// 导入保护路由组件
import ProtectedRoute from "../components/ProtectedRoute";
export default function Home() {
return (
<Box minH="100vh">
{/* 导航栏 - 这是关键确保HomeNavbar被正确包含 */}
<HomeNavbar />
{/* 主要内容区域 */}
<Box>
<Routes>
{/* 首页默认路由 */}
<Route path="/" element={<HomePage />} />
<Route path="/dashboard" element={<HomePage />} />
<Route
path="/center"
element={
<ProtectedRoute>
<CenterDashboard />
</ProtectedRoute>
}
/>
{/* 需要登录保护的页面 */}
<Route
path="/profile"
element={
<ProtectedRoute>
<ProfilePage />
</ProtectedRoute>
}
/>
<Route
path="/settings"
element={
<ProtectedRoute>
<SettingsPage />
</ProtectedRoute>
}
/>
{/* 订阅管理页面 */}
<Route
path="/pages/account/subscription"
element={
<ProtectedRoute>
<Subscription />
</ProtectedRoute>
}
/>
{/* 模拟盘交易页面 */}
<Route
path="/trading-simulation"
element={
<ProtectedRoute>
<TradingSimulation />
</ProtectedRoute>
}
/>
{/* 其他可能的路由 */}
<Route path="*" element={<HomePage />} />
</Routes>
</Box>
</Box>
);
}

91
src/layouts/Landing.js Normal file
View File

@@ -0,0 +1,91 @@
// Chakra imports
import { Box, Stack } from "@chakra-ui/react";
import { ArgonLogoLight, ChakraLogoLight } from "components/Icons/Icons";
import MainPanel from "components/Layout/MainPanel";
import PanelContainer from "components/Layout/PanelContainer";
import PanelContent from "components/Layout/PanelContent";
import Sidebar from "components/Sidebar/Sidebar.js";
import { SidebarContext } from "contexts/SidebarContext";
import React, { useState } from "react";
import { Route, Routes, Navigate } from "react-router-dom";
import routes from "routes.js";
const Landing = () => {
const [toggleSidebar, setToggleSidebar] = useState(false);
const [sidebarWidth, setSidebarWidth] = useState(275);
const getRoutes = (routes) => {
return routes.map((route, key) => {
if (route.layout === "/landing") {
return (
<Route
path={ route.path}
element={route.component}
key={key}
/>
);
}
if (route.collapse) {
return getRoutes(route.items);
}
if (route.category) {
return getRoutes(route.items);
} else {
return null;
}
});
};
return (
<SidebarContext.Provider
value={{
sidebarWidth,
setSidebarWidth,
toggleSidebar,
setToggleSidebar,
}}
>
<Box minH="100vh" bg="linear-gradient(180deg, #3182CE 0%, #63B3ED 100%)">
<Sidebar
routes={routes}
landing={true}
logo={
<Stack
direction="row"
spacing="12px"
align="center"
justify="center"
>
<ArgonLogoLight w="74px" h="27px" />
<Box w="1px" h="20px" bg={"white"} />
<ChakraLogoLight w="82px" h="21px" />
</Stack>
}
display="none"
/>
<MainPanel
w={{
base: "100%",
xl: `calc(100% - 275px)`,
}}
minH="100vh"
>
<PanelContent>
<PanelContainer>
<Routes>
{getRoutes(routes)}
<Route
path="/landing"
element={<Navigate to="/admin/dashboard/default" replace />}
/>
</Routes>
</PanelContainer>
</PanelContent>
</MainPanel>
</Box>
</SidebarContext.Provider>
);
};
export default Landing;

251
src/layouts/RTL.js Normal file
View File

@@ -0,0 +1,251 @@
/*!
=========================================================
* 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 {
Portal,
Box,
useColorMode,
Stack,
useDisclosure,
useColorModeValue,
} from "@chakra-ui/react";
import Configurator from "components/Configurator/Configurator";
import FixedPlugin from "components/FixedPlugin/FixedPlugin";
import Footer from "components/Footer/Footer.js";
// Custom components
import MainPanel from "components/Layout/MainPanel";
import PanelContainer from "components/Layout/PanelContainer";
import PanelContent from "components/Layout/PanelContent";
// Layout components
import AdminNavbar from "components/Navbars/AdminNavbar.js";
import Sidebar from "components/Sidebar/Sidebar.js";
import { SidebarContext } from "contexts/SidebarContext";
import React, { useState } from "react";
import "react-quill/dist/quill.snow.css"; // ES6
import { Route, Routes, Navigate } from "react-router-dom";
import routes from "routes.js";
import {
ArgonLogoDark,
ArgonLogoLight,
ChakraLogoDark,
ChakraLogoLight,
ArgonLogoMinifiedDark,
ArgonLogoMinifiedLight,
} from "components/Icons/Icons";
import { RtlProvider } from "components/RTLProvider/RTLProvider";
export default function Dashboard(props) {
const { ...rest } = props;
// states and functions
const [sidebarVariant, setSidebarVariant] = useState("transparent");
const [fixed, setFixed] = useState(false);
const [toggleSidebar, setToggleSidebar] = useState(false);
const [sidebarWidth, setSidebarWidth] = useState(275);
// ref for main panel div
const mainPanel = React.createRef();
const { colorMode } = useColorMode();
// functions for changing the states from components
const getRoute = () => {
return window.location.pathname !== "/rtl/full-screen-maps";
};
const getActiveRoute = (routes) => {
let activeRoute = "Default Brand Text";
for (let i = 0; i < routes.length; i++) {
if (routes[i].collapse) {
let collapseActiveRoute = getActiveRoute(routes[i].items);
if (collapseActiveRoute !== activeRoute) {
return collapseActiveRoute;
}
} else if (routes[i].category) {
let categoryActiveRoute = getActiveRoute(routes[i].items);
if (categoryActiveRoute !== activeRoute) {
return categoryActiveRoute;
}
} else {
if (
window.location.href.indexOf(routes[i].layout + routes[i].path) !== -1
) {
return routes[i].name;
}
}
}
return activeRoute;
};
// This changes navbar state(fixed or not)
const getActiveNavbar = (routes) => {
let activeNavbar = false;
for (let i = 0; i < routes.length; i++) {
if (routes[i].category) {
let categoryActiveNavbar = getActiveNavbar(routes[i].items);
if (categoryActiveNavbar !== activeNavbar) {
return categoryActiveNavbar;
}
} else {
if (
window.location.href.indexOf(routes[i].layout + routes[i].path) !== -1
) {
if (routes[i].secondaryNavbar) {
return routes[i].secondaryNavbar;
}
}
}
}
return activeNavbar;
};
const getRoutes = (routes) => {
return routes.map((route, key) => {
if (route.layout === "/rtl") {
return (
<Route
path={ route.path}
element={route.component}
key={key}
/>
);
}
if (route.collapse) {
return getRoutes(route.items);
}
if (route.category) {
return getRoutes(route.items);
} else {
return null;
}
});
};
const { isOpen, onOpen, onClose } = useDisclosure();
document.body.style.backgroundColor = useColorModeValue(
"gray.50",
"gray.800"
);
document.documentElement.dir = "rtl";
document.documentElement.layout = "rtl";
let bgBoxHeight = "40vh";
let bgBoxColor = useColorModeValue("blue.500", "navy.900");
// Chakra Color Mode
return (
<>
<RtlProvider>
<SidebarContext.Provider
value={{
sidebarWidth,
setSidebarWidth,
toggleSidebar,
setToggleSidebar,
}}>
<Box
minH={bgBoxHeight}
h='100% !important'
w='100%'
position='absolute'
bg={bgBoxColor}
top='0'
/>
<Sidebar
routes={routes}
logo={
sidebarWidth === 275 ? (
<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 === "light" ? (
<ArgonLogoMinifiedDark w='36px' h='36px' />
) : (
<ArgonLogoMinifiedLight w='36px' h='36px' />
)
}
display='none'
{...rest}
/>
<MainPanel
ref={mainPanel}
w={{
base: "100%",
xl: `calc(100% - ${sidebarWidth}px)`,
}}
variant='rtl'>
<Portal>
<Box>
<AdminNavbar
onOpen={onOpen}
logoText={"Argon Dashboard Chakra PRO"}
brandText={getActiveRoute(routes)}
secondary={getActiveNavbar(routes)}
fixed={fixed}
{...rest}
/>
</Box>
</Portal>
{getRoute() ? (
<PanelContent>
<PanelContainer>
<Routes>
{getRoutes(routes)}
<Route
path="/"
element={<Navigate to="/admin/dashboard/default" replace />}
/>
</Routes>
</PanelContainer>
</PanelContent>
) : null}
<Box>
<Footer />
</Box>
<Portal>
<Box>
<FixedPlugin fixed={fixed} onOpen={onOpen} />
</Box>
</Portal>
<Configurator
secondary={getActiveNavbar(routes)}
isOpen={isOpen}
onClose={onClose}
isChecked={fixed}
onSwitch={(value) => {
setFixed(value);
}}
/>
</MainPanel>
</SidebarContext.Provider>
</RtlProvider>
</>
);
}