feat: 添加 oauth, 添加第三方登录入口

This commit is contained in:
Gavan
2025-07-10 18:01:58 +08:00
parent 06f9c45797
commit 0776e5eb54
27 changed files with 2115 additions and 378 deletions

View File

@@ -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>
);
};

View File

@@ -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>
);

View File

@@ -108,7 +108,7 @@ const Code = ({
accessibilitySupport: 'off',
bracketPairColorization: { enabled: false },
matchBrackets: 'never',
lineNumbers: 'on',
lineNumbers: 'off',
verticalScrollbarSize: 0,
horizontalScrollbarSize: 0,
scrollbar: {

View File

@@ -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,

View File

@@ -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={{