mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-14 12:43:26 +08:00
feat: 添加 oauth, 添加第三方登录入口
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { styled, FormLabel } from '@mui/material';
|
||||
import { styled, FormLabel, Box } from '@mui/material';
|
||||
|
||||
export const StyledFormLabel = styled(FormLabel)(({ theme }) => ({
|
||||
display: 'block',
|
||||
@@ -10,3 +10,20 @@ export const StyledFormLabel = styled(FormLabel)(({ theme }) => ({
|
||||
fontSize: 14,
|
||||
},
|
||||
}));
|
||||
|
||||
export const FormItem = ({
|
||||
label,
|
||||
children,
|
||||
required,
|
||||
}: {
|
||||
label: string;
|
||||
children: React.ReactNode;
|
||||
required?: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<StyledFormLabel required={required}>{label}</StyledFormLabel>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,111 +1,121 @@
|
||||
import KeyboardArrowRightRoundedIcon from '@mui/icons-material/KeyboardArrowRightRounded';
|
||||
import { Box, Stack, useTheme } from '@mui/material';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Box, Stack, Typography } from '@mui/material';
|
||||
import { useMemo } from 'react';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
|
||||
const HomeBread = { title: '工作台', to: '/' };
|
||||
const OtherBread = {
|
||||
const ADMIN_BREADCRUMB_MAP: Record<string, { title: string; to: string }> = {
|
||||
dashboard: { title: '仪表盘', to: '/' },
|
||||
chat: { title: '对话记录', to: '/chat' },
|
||||
completion: { title: '补全记录', to: '/completion' },
|
||||
model: { title: '模型管理', to: '/model' },
|
||||
user: { title: '成员管理', to: '/user' },
|
||||
'user-management': { title: '成员管理', to: '/user-management' },
|
||||
admin: { title: '管理员', to: '/admin' },
|
||||
};
|
||||
|
||||
const Bread = () => {
|
||||
const theme = useTheme();
|
||||
const { pathname } = useLocation();
|
||||
const [breads, setBreads] = useState<
|
||||
{ title: React.ReactNode; to: string }[]
|
||||
>([]);
|
||||
const USER_BREADCRUMB_MAP: Record<string, { title: string; to: string }> = {
|
||||
dashboard: { title: '仪表盘', to: '/user/dashboard' },
|
||||
chat: { title: '对话记录', to: '/user/chat' },
|
||||
completion: { title: '补全记录', to: '/user/completion' },
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const curBreads: { title: React.ReactNode; to: string }[] = [
|
||||
{
|
||||
title: (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
}}
|
||||
>
|
||||
MonkeyCode
|
||||
</Box>
|
||||
),
|
||||
to: '/dashboard',
|
||||
},
|
||||
];
|
||||
if (pathname === '/') {
|
||||
curBreads.push(HomeBread);
|
||||
} else {
|
||||
const pieces = pathname.split('/').filter((it) => it !== '');
|
||||
pieces.forEach((it) => {
|
||||
const bread = OtherBread[it as keyof typeof OtherBread];
|
||||
if (bread) {
|
||||
curBreads.push(bread);
|
||||
const Bread = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const breadcrumbs = useMemo(() => {
|
||||
const pathParts = pathname.split('/').filter(Boolean);
|
||||
|
||||
const generatedCrumbs = pathParts
|
||||
.map((part) => {
|
||||
if (pathname.startsWith('/user/')) {
|
||||
return USER_BREADCRUMB_MAP[part];
|
||||
}
|
||||
});
|
||||
}
|
||||
// if (pageName) {
|
||||
// curBreads.push({ title: pageName, to: 'custom' })
|
||||
// }
|
||||
setBreads(curBreads);
|
||||
return ADMIN_BREADCRUMB_MAP[part];
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return [
|
||||
{
|
||||
title: 'MonkeyCode',
|
||||
to: pathname.startsWith('/user/') ? '/user/dashboard' : '/dashboard',
|
||||
},
|
||||
...generatedCrumbs,
|
||||
];
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction={'row'}
|
||||
alignItems={'center'}
|
||||
direction='row'
|
||||
alignItems='center'
|
||||
gap={1}
|
||||
component='nav'
|
||||
aria-label='breadcrumb'
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
// color: 'text.auxiliary',
|
||||
fontSize: '14px',
|
||||
// a: { color: 'text.auxiliary' },
|
||||
}}
|
||||
>
|
||||
{/* <KBSelect /> */}
|
||||
{breads.map((it, idx) => {
|
||||
return (
|
||||
<Stack
|
||||
direction={'row'}
|
||||
alignItems={'center'}
|
||||
gap={1}
|
||||
key={idx}
|
||||
sx={{
|
||||
color:
|
||||
idx === breads.length - 1
|
||||
? `${theme.palette.text.primary} !important`
|
||||
: 'text.disabled',
|
||||
{breadcrumbs.map((crumb, index) => {
|
||||
const isLast = index === breadcrumbs.length - 1;
|
||||
|
||||
...(idx === breads.length - 1 && { fontWeight: 'bold' }),
|
||||
}}
|
||||
>
|
||||
{idx !== 0 && (
|
||||
const crumbContent = (
|
||||
<Stack direction='row' alignItems='center' gap={1}>
|
||||
{index > 0 && (
|
||||
<KeyboardArrowRightRoundedIcon sx={{ fontSize: 14 }} />
|
||||
)}
|
||||
{it.to === 'custom' ? (
|
||||
<Box
|
||||
sx={{ cursor: 'pointer', ':hover': { color: 'primary.main' } }}
|
||||
>
|
||||
{it.title}
|
||||
</Box>
|
||||
) : (
|
||||
<NavLink to={it.to}>
|
||||
<Box
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
':hover': { color: 'primary.main' },
|
||||
}}
|
||||
>
|
||||
{it.title}
|
||||
</Box>
|
||||
</NavLink>
|
||||
)}
|
||||
<Typography
|
||||
variant='body2'
|
||||
sx={{
|
||||
fontWeight: isLast ? 'bold' : 'normal',
|
||||
}}
|
||||
>
|
||||
{crumb.title}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
if (isLast) {
|
||||
return (
|
||||
<Box key={index} sx={{ color: 'text.primary' }}>
|
||||
{crumbContent}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (crumb.to === 'custom') {
|
||||
return (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
color: 'text.disabled',
|
||||
cursor: 'pointer',
|
||||
'&:hover': {
|
||||
color: 'primary.main',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{crumbContent}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
key={index}
|
||||
to={crumb.to}
|
||||
style={{ textDecoration: 'none', color: 'inherit' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
color: 'text.disabled',
|
||||
'&:hover': {
|
||||
color: 'primary.main',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{crumbContent}
|
||||
</Box>
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -108,7 +108,7 @@ const Code = ({
|
||||
accessibilitySupport: 'off',
|
||||
bracketPairColorization: { enabled: false },
|
||||
matchBrackets: 'never',
|
||||
lineNumbers: 'on',
|
||||
lineNumbers: 'off',
|
||||
verticalScrollbarSize: 0,
|
||||
horizontalScrollbarSize: 0,
|
||||
scrollbar: {
|
||||
|
||||
@@ -61,7 +61,7 @@ const Diff: React.FC<DiffProps> = ({
|
||||
fontSize: 14,
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'off',
|
||||
lineNumbers: 'on',
|
||||
lineNumbers: 'off',
|
||||
glyphMargin: false,
|
||||
folding: false,
|
||||
overviewRulerLanes: 0,
|
||||
|
||||
@@ -2,13 +2,12 @@ import Logo from '@/assets/images/logo.png';
|
||||
import { alpha, Box, Button, Stack, useTheme, styled } from '@mui/material';
|
||||
import { Icon } from '@c-x/ui';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
import Avatar from '../avatar';
|
||||
import { Modal } from '@c-x/ui';
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import Qrcode from '@/assets/images/qrcode.png';
|
||||
import Version from './version';
|
||||
|
||||
const menus = [
|
||||
const ADMIN_MENUS = [
|
||||
{
|
||||
label: '仪表盘',
|
||||
value: '/dashboard',
|
||||
@@ -46,8 +45,8 @@ const menus = [
|
||||
},
|
||||
{
|
||||
label: '成员管理',
|
||||
value: '/user',
|
||||
pathname: 'user',
|
||||
value: '/user-management',
|
||||
pathname: 'user-management',
|
||||
icon: 'icon-yonghuguanli1',
|
||||
show: true,
|
||||
},
|
||||
@@ -60,6 +59,30 @@ const menus = [
|
||||
},
|
||||
];
|
||||
|
||||
const USER_MENUS = [
|
||||
{
|
||||
label: '仪表盘',
|
||||
value: '/user/dashboard',
|
||||
pathname: '/user/dashboard',
|
||||
icon: 'icon-yibiaopan',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
label: '对话记录',
|
||||
value: '/user/chat',
|
||||
pathname: '/user/chat',
|
||||
icon: 'icon-duihuajilu1',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
label: '补全记录',
|
||||
value: '/user/completion',
|
||||
pathname: '/user/completion',
|
||||
icon: 'icon-buquanjilu',
|
||||
show: true,
|
||||
},
|
||||
];
|
||||
|
||||
const SidebarButton = styled(Button)(({ theme }) => ({
|
||||
fontSize: 14,
|
||||
flexShrink: 0,
|
||||
@@ -81,6 +104,13 @@ const Sidebar = () => {
|
||||
const { pathname } = useLocation();
|
||||
const theme = useTheme();
|
||||
const [showQrcode, setShowQrcode] = useState(false);
|
||||
const menus = useMemo(() => {
|
||||
if (pathname.startsWith('/user/')) {
|
||||
return USER_MENUS;
|
||||
}
|
||||
return ADMIN_MENUS;
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
sx={{
|
||||
|
||||
Reference in New Issue
Block a user