feat: add dark mod (#134)

This commit is contained in:
Buer 2024-04-01 18:41:38 +08:00 committed by GitHub
parent 6300ce4835
commit 7c2b878892
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 242 additions and 75 deletions

View File

@ -1,42 +1,42 @@
{
"name": "one_api_web",
"version": "1.0.0",
"version": "1.2.0",
"proxy": "http://127.0.0.1:3000",
"private": true,
"homepage": "",
"dependencies": {
"@emotion/cache": "^11.9.3",
"@emotion/react": "^11.9.3",
"@emotion/styled": "^11.9.3",
"@mui/icons-material": "^5.8.4",
"@mui/lab": "^5.0.0-alpha.169",
"@mui/material": "^5.8.6",
"@mui/system": "^5.8.6",
"@mui/utils": "^5.8.6",
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.7",
"@mui/lab": "^5.0.0-alpha.163",
"@mui/material": "^5.15.7",
"@mui/system": "^5.15.7",
"@mui/utils": "^5.15.7",
"@mui/x-data-grid": "^6.19.4",
"@mui/x-date-pickers": "^6.18.5",
"@tabler/icons-react": "^2.44.0",
"apexcharts": "^3.35.3",
"@tabler/icons-react": "^2.46.0",
"apexcharts": "^3.45.2",
"axios": "^0.27.2",
"dayjs": "^1.11.10",
"formik": "^2.2.9",
"framer-motion": "^6.3.16",
"formik": "^2.4.5",
"framer-motion": "^11.0.3",
"history": "^5.3.0",
"marked": "^4.1.1",
"material-ui-popup-state": "^4.0.1",
"material-ui-popup-state": "^5.0.10",
"notistack": "^3.0.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-apexcharts": "^1.4.0",
"react-device-detect": "^2.2.2",
"react-apexcharts": "^1.4.1",
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
"react-perfect-scrollbar": "^1.5.8",
"react-redux": "^8.0.2",
"react-router": "6.3.0",
"react-router-dom": "6.3.0",
"react-redux": "^9.1.0",
"react-router": "6.21.3",
"react-router-dom": "6.21.3",
"react-scripts": "^5.0.1",
"react-turnstile": "^1.1.2",
"redux": "^4.2.0",
"redux": "^5.0.1",
"yup": "^0.32.11"
},
"scripts": {
@ -67,19 +67,20 @@
]
},
"devDependencies": {
"@babel/core": "^7.21.4",
"@babel/eslint-parser": "^7.21.3",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",
"@babel/core": "^7.23.9",
"@babel/eslint-parser": "^7.23.10",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"immutable": "^4.3.0",
"prettier": "^2.8.7",
"sass": "^1.53.0"
"immutable": "^4.3.5",
"prettier": "^3.2.4",
"sass": "^1.70.0"
}
}

View File

@ -1,8 +1,9 @@
import { useSelector } from 'react-redux';
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { ThemeProvider } from '@mui/material/styles';
import { CssBaseline, StyledEngineProvider } from '@mui/material';
import { SET_THEME } from 'store/actions';
// routing
import Routes from 'routes';
@ -21,8 +22,16 @@ import CopySnackbar from 'ui-component/Snackbar';
// ==============================|| APP ||============================== //
const App = () => {
const dispatch = useDispatch();
const customization = useSelector((state) => state.customization);
useEffect(() => {
const storedTheme = localStorage.getItem('theme');
if (storedTheme) {
dispatch({ type: SET_THEME, theme: storedTheme });
}
}, [dispatch]);
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={themes(customization)}>

View File

@ -46,11 +46,16 @@ $grey600: #4b5565;
$grey700: #364152;
$grey900: #121926;
$tableBackground: #f4f6f8;
$tableBorderBottom: #f1f3f4;
// ==============================|| DARK THEME VARIANTS ||============================== //
// paper & background
$darkBackground: #1a223f; // level 3
$darkPaper: #111936; // level 4
$darkDivider: rgba(227, 232, 239, 0.2);
$darkSelectedBack : rgba(124, 77, 255, 0.15);
// dark 800 & 900
$darkLevel1: #29314f; // level 1
@ -154,4 +159,9 @@ $darkTextSecondary: #8492c4;
darkSecondaryDark: $darkSecondaryDark;
darkSecondary200: $darkSecondary200;
darkSecondary800: $darkSecondary800;
darkDivider: $darkDivider;
darkSelectedBack: $darkSelectedBack;
tableBackground: $tableBackground;
tableBorderBottom: $tableBorderBottom;
}

View File

@ -71,8 +71,8 @@ const ProfileSection = () => {
alignItems: 'center',
borderRadius: '27px',
transition: 'all .2s ease-in-out',
borderColor: theme.palette.primary.light,
backgroundColor: theme.palette.primary.light,
borderColor: theme.typography.menuChip.background,
backgroundColor: theme.typography.menuChip.background,
'&[aria-controls="menu-list-grow"], &:hover': {
borderColor: theme.palette.primary.main,
background: `${theme.palette.primary.main}!important`,

View File

@ -7,6 +7,7 @@ import { Avatar, Box, ButtonBase } from '@mui/material';
// project imports
import LogoSection from '../LogoSection';
import ProfileSection from './ProfileSection';
import ThemeButton from 'ui-component/ThemeButton';
// assets
import { IconMenu2 } from '@tabler/icons-react';
@ -37,9 +38,8 @@ const Header = ({ handleLeftDrawerToggle }) => {
sx={{
...theme.typography.commonAvatar,
...theme.typography.mediumAvatar,
...theme.typography.menuButton,
transition: 'all .2s ease-in-out',
background: theme.palette.secondary.light,
color: theme.palette.secondary.dark,
'&:hover': {
background: theme.palette.secondary.dark,
color: theme.palette.secondary.light
@ -55,7 +55,7 @@ const Header = ({ handleLeftDrawerToggle }) => {
<Box sx={{ flexGrow: 1 }} />
<Box sx={{ flexGrow: 1 }} />
<ThemeButton />
<ProfileSection />
</>
);

View File

@ -36,7 +36,7 @@ import { useNavigate } from 'react-router-dom';
// }));
const CardStyle = styled(Card)(({ theme }) => ({
background: theme.palette.primary.light,
background: theme.typography.menuChip.background,
marginBottom: '22px',
overflow: 'hidden',
position: 'relative',

View File

@ -38,6 +38,15 @@ const Sidebar = ({ drawerOpen, drawerToggle, window }) => {
>
<MenuList />
<MenuCard />
<Stack direction="row" justifyContent="center" sx={{ mb: 2 }}>
<Chip
label={process.env.REACT_APP_VERSION || '未知版本'}
disabled
chipcolor="secondary"
size="small"
sx={{ cursor: 'pointer' }}
/>
</Stack>
</PerfectScrollbar>
</BrowserView>
<MobileView>
@ -45,7 +54,13 @@ const Sidebar = ({ drawerOpen, drawerToggle, window }) => {
<MenuList />
<MenuCard />
<Stack direction="row" justifyContent="center" sx={{ mb: 2 }}>
<Chip label={process.env.REACT_APP_VERSION} disabled chipcolor="secondary" size="small" sx={{ cursor: 'pointer' }} />
<Chip
label={process.env.REACT_APP_VERSION || '未知版本'}
disabled
chipcolor="secondary"
size="small"
sx={{ cursor: 'pointer' }}
/>
</Stack>
</Box>
</MobileView>

View File

@ -5,6 +5,7 @@ import LogoSection from 'layout/MainLayout/LogoSection';
import { Link } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
import { useSelector } from 'react-redux';
import ThemeButton from 'ui-component/ThemeButton';
// ==============================|| MAIN NAVBAR / HEADER ||============================== //
@ -38,6 +39,7 @@ const Header = () => {
<Button component={Link} variant="text" to="/about" color={pathname === '/about' ? 'primary' : 'inherit'}>
关于
</Button>
<ThemeButton />
{account.user ? (
<Button component={Link} variant="contained" to="/panel" color="primary">
控制台

View File

@ -7,3 +7,4 @@ export const SET_BORDER_RADIUS = '@customization/SET_BORDER_RADIUS';
export const SET_SITE_INFO = '@siteInfo/SET_SITE_INFO';
export const LOGIN = '@account/LOGIN';
export const LOGOUT = '@account/LOGOUT';
export const SET_THEME = '@customization/SET_THEME';

View File

@ -9,7 +9,8 @@ export const initialState = {
defaultId: 'default',
fontFamily: config.fontFamily,
borderRadius: config.borderRadius,
opened: true
opened: true,
theme: 'light'
};
// ==============================|| CUSTOMIZATION REDUCER ||============================== //
@ -38,6 +39,11 @@ const customizationReducer = (state = initialState, action) => {
...state,
borderRadius: action.borderRadius
};
case actionTypes.SET_THEME:
return {
...state,
theme: action.theme
};
default:
return state;
}

View File

@ -1,5 +1,5 @@
export default function componentStyleOverrides(theme) {
const bgColor = theme.colors?.grey50;
const bgColor = theme.mode === 'dark' ? theme.backgroundDefault : theme.colors?.grey50;
return {
MuiButton: {
styleOverrides: {
@ -226,12 +226,12 @@ export default function componentStyleOverrides(theme) {
MuiTableCell: {
styleOverrides: {
root: {
borderBottom: '1px solid rgb(241, 243, 244)',
borderBottom: '1px solid ' + theme.tableBorderBottom,
textAlign: 'center'
},
head: {
color: theme.darkTextSecondary,
backgroundColor: 'rgb(244, 246, 248)'
backgroundColor: theme.headBackgroundColor
}
}
},
@ -239,7 +239,7 @@ export default function componentStyleOverrides(theme) {
styleOverrides: {
root: {
'&:hover': {
backgroundColor: 'rgb(244, 246, 248)'
backgroundColor: theme.headBackgroundColor
}
}
}
@ -251,6 +251,22 @@ export default function componentStyleOverrides(theme) {
background: theme.colors?.grey700
}
}
},
MuiCssBaseline: {
styleOverrides: `
.apexcharts-title-text {
fill: ${theme.textDark} !important
}
.apexcharts-text {
fill: ${theme.textDark} !important
}
.apexcharts-legend-text {
color: ${theme.textDark} !important
}
.apexcharts-menu {
background: ${theme.backgroundDefault} !important
}
`
}
};
}

View File

@ -15,19 +15,10 @@ import themeTypography from './typography';
export const theme = (customization) => {
const color = colors;
const options = customization.theme === 'light' ? GetLightOption() : GetDarkOption();
const themeOption = {
colors: color,
heading: color.grey900,
paper: color.paper,
backgroundDefault: color.paper,
background: color.primaryLight,
darkTextPrimary: color.grey700,
darkTextSecondary: color.grey500,
textDark: color.grey900,
menuSelected: color.secondaryDark,
menuSelectedBack: color.secondaryLight,
divider: color.grey200,
...options,
customization
};
@ -53,3 +44,49 @@ export const theme = (customization) => {
};
export default theme;
function GetDarkOption() {
const color = colors;
return {
mode: 'dark',
heading: color.darkTextTitle,
paper: color.darkLevel2,
backgroundDefault: color.darkPaper,
background: color.darkBackground,
darkTextPrimary: color.darkTextPrimary,
darkTextSecondary: color.darkTextSecondary,
textDark: color.darkTextTitle,
menuSelected: color.darkSecondaryMain,
menuSelectedBack: color.darkSelectedBack,
divider: color.darkDivider,
borderColor: color.darkBorderColor,
menuButton: color.darkLevel1,
menuButtonColor: color.darkSecondaryMain,
menuChip: color.darkLevel1,
headBackgroundColor: color.darkBackground,
tableBorderBottom: color.darkDivider
};
}
function GetLightOption() {
const color = colors;
return {
mode: 'light',
heading: color.grey900,
paper: color.paper,
backgroundDefault: color.paper,
background: color.primaryLight,
darkTextPrimary: color.grey700,
darkTextSecondary: color.grey500,
textDark: color.grey900,
menuSelected: color.secondaryDark,
menuSelectedBack: color.secondaryLight,
divider: color.grey200,
borderColor: color.grey300,
menuButton: color.secondaryLight,
menuButtonColor: color.secondaryDark,
menuChip: color.primaryLight,
headBackgroundColor: color.tableBackground,
tableBorderBottom: color.tableBorderBottom
};
}

View File

@ -5,7 +5,7 @@
export default function themePalette(theme) {
return {
mode: 'light',
mode: theme.mode,
common: {
black: theme.colors?.darkPaper
},

View File

@ -132,6 +132,19 @@ export default function themeTypography(theme) {
width: '44px',
height: '44px',
fontSize: '1.5rem'
},
menuButton: {
color: theme.menuButtonColor,
background: theme.menuButton
},
menuChip: {
background: theme.menuChip
},
CardWrapper: {
backgroundColor: theme.mode === 'dark' ? theme.colors.darkLevel2 : theme.colors.primaryDark
},
SubCard: {
border: theme.mode === 'dark' ? '1px solid rgba(227, 232, 239, 0.2)' : '1px solid rgb(227, 232, 239)'
}
};
}

View File

@ -5,11 +5,10 @@ import Loader from './Loader';
// ==============================|| LOADABLE - LAZY LOADING ||============================== //
const Loadable = (Component) => (props) =>
(
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
const Loadable = (Component) => (props) => (
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
export default Loadable;

View File

@ -1,6 +1,8 @@
// material-ui
import logo from 'assets/images/logo.svg';
import logoLight from 'assets/images/logo.svg';
import logoDark from 'assets/images/logo-white.svg';
import { useSelector } from 'react-redux';
import { useTheme } from '@mui/material/styles';
/**
* if you want to use image instead of <svg> uncomment following.
@ -14,6 +16,8 @@ import { useSelector } from 'react-redux';
const Logo = () => {
const siteInfo = useSelector((state) => state.siteInfo);
const theme = useTheme();
const logo = theme.palette.mode === 'light' ? logoLight : logoDark;
return <img src={siteInfo.logo || logo} alt={siteInfo.system_name} height="50" />;
};

View File

@ -0,0 +1,50 @@
import { useDispatch, useSelector } from 'react-redux';
import { SET_THEME } from 'store/actions';
import { useTheme } from '@mui/material/styles';
import { Avatar, Box, ButtonBase } from '@mui/material';
import { IconSun, IconMoon } from '@tabler/icons-react';
export default function ThemeButton() {
const dispatch = useDispatch();
const defaultTheme = useSelector((state) => state.customization.theme);
const theme = useTheme();
return (
<Box
sx={{
ml: 2,
mr: 3,
[theme.breakpoints.down('md')]: {
mr: 2
}
}}
>
<ButtonBase sx={{ borderRadius: '12px' }}>
<Avatar
variant="rounded"
sx={{
...theme.typography.commonAvatar,
...theme.typography.mediumAvatar,
transition: 'all .2s ease-in-out',
borderColor: theme.typography.menuChip.background,
backgroundColor: theme.typography.menuChip.background,
'&[aria-controls="menu-list-grow"],&:hover': {
background: theme.palette.secondary.dark,
color: theme.palette.secondary.light
}
}}
onClick={() => {
let theme = defaultTheme === 'light' ? 'dark' : 'light';
dispatch({ type: SET_THEME, theme: theme });
localStorage.setItem('theme', theme);
}}
color="inherit"
>
{defaultTheme === 'light' ? <IconSun stroke={1.5} size="1.3rem" /> : <IconMoon stroke={1.5} size="1.3rem" />}
</Avatar>
</ButtonBase>
</Box>
);
}

View File

@ -15,7 +15,7 @@ const headerSX = {
const MainCard = forwardRef(
(
{
border = true,
border = false,
boxShadow,
children,
content = true,

View File

@ -15,8 +15,7 @@ const SubCard = forwardRef(
<Card
ref={ref}
sx={{
border: '1px solid',
borderColor: theme.palette.primary.light,
border: theme.typography.SubCard.border,
':hover': {
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)'
},
@ -36,8 +35,8 @@ const SubCard = forwardRef(
{title && (
<Divider
sx={{
opacity: 1,
borderColor: theme.palette.primary.light
opacity: 1
// borderColor: theme.palette.primary.light
}}
/>
)}

View File

@ -40,7 +40,8 @@ export function generateLineChartOptions(data, unit) {
chart: {
sparkline: {
enabled: true
}
},
background: 'transparent'
},
dataLabels: {
enabled: false
@ -139,7 +140,8 @@ export function generateBarChartOptions(xaxis, data, unit = '', decimal = 0) {
},
zoom: {
enabled: true
}
},
background: 'transparent'
},
responsive: [
{

View File

@ -100,7 +100,8 @@ const chartData = {
},
zoom: {
enabled: true
}
},
background: 'transparent'
},
responsive: [
{

View File

@ -229,7 +229,8 @@ function getBarChartOptions(data, dateRange) {
type: 'line',
zoom: {
enabled: false
}
},
background: 'transparent'
};
channelData.latency.options.stroke = {
curve: 'smooth',
@ -266,7 +267,8 @@ function getRedemptionData(data, dateRange) {
height: 480,
options: {
chart: {
type: 'line'
type: 'line',
background: 'transparent'
},
stroke: {
width: [0, 4]

View File

@ -12,7 +12,7 @@ import MainCard from 'ui-component/cards/MainCard';
import SkeletonTotalOrderCard from 'ui-component/cards/Skeleton/EarningCard';
const CardWrapper = styled(MainCard)(({ theme }) => ({
backgroundColor: theme.palette.primary.dark,
...theme.typography.CardWrapper,
color: '#fff',
overflow: 'hidden',
position: 'relative',