Merge pull request #95 from safe1ine/main

增加了管理员登录记录
This commit is contained in:
safe1ine
2025-07-16 20:40:17 +08:00
committed by GitHub
7 changed files with 57 additions and 46 deletions

View File

@@ -380,6 +380,8 @@ export interface DomainModel {
input?: number;
/** 是否启用 */
is_active?: boolean;
/** 是否内部模型 */
is_internal?: boolean;
/** 模型名称 如: deepseek-v3 */
model_name?: string;
/** 模型类型 llm:对话模型 coder:代码模型 */

View File

@@ -189,8 +189,11 @@ const AdminTable = () => {
{
title: '最近活跃时间',
dataIndex: 'last_active_at',
render: (text) => {
return text === 0 ? '从未使用' : dayjs.unix(text).fromNow();
render: (text, record) => {
return <Stack direction='column'>
<Box sx={{ color: 'text.secondary' }}>{record.created_at ? dayjs.unix(record.created_at).fromNow() + '加入' : '加入时间未知'}</Box>
<Box sx={{ color: 'text.secondary' }}>{record.last_active_at ? dayjs.unix(record.last_active_at).fromNow() + '活跃' : '活跃时间未知'}</Box>
</Stack>
},
},
{
@@ -212,14 +215,14 @@ const AdminTable = () => {
},
];
return (
<Card>
<Card sx={{ height: '100%' }}>
<Stack
direction='row'
justifyContent='space-between'
alignItems='center'
sx={{ mb: 2 }}
>
<Box sx={{ fontWeight: 700 }}></Box>
<Box sx={{ fontWeight: 700, lineHeight: '36px' }}></Box>
<Button
variant='contained'
color='primary'

View File

@@ -1,14 +1,18 @@
import React from 'react';
import LoginHistory from './loginHistory';
import AdminTable from './adminTable';
import { Stack } from '@mui/material';
import { Grid2 as Grid, Stack } from '@mui/material';
const Admin = () => {
return (
<Stack gap={2} sx={{ height: '100%' }}>
<AdminTable />
<LoginHistory />
</Stack>
<Grid container spacing={2} sx={{ height: '100%' }}>
<Grid size={6}>
<AdminTable />
</Grid>
<Grid size={6}>
<LoginHistory />
</Grid>
</Grid>
);
};

View File

@@ -23,17 +23,15 @@ const LoginHistory = () => {
},
},
{
title: 'IP 地址',
title: 'IP 地址',
dataIndex: 'ip',
render: (ip, record) => {
let address = '';
if (record?.ip_info) {
address = `${record?.ip_info?.country}-${record?.ip_info?.city}`;
}
return (
<Stack direction='row'>
<Box>{ip}</Box>
<Box sx={{ color: 'text.secondary' }}>{address}</Box>
<Stack direction='column'>
<Box>{record?.ip_info?.ip}</Box>
<Box sx={{ color: 'text.secondary' }}>
{record?.ip_info?.country === '中国' ? ('' + record?.ip_info?.province + '-' + record?.ip_info?.city) : (record?.ip_info?.country || '未知')}
</Box>
</Stack>
);
},
@@ -47,14 +45,14 @@ const LoginHistory = () => {
},
];
return (
<Card>
<Card sx={{ height: '100%' }}>
<Stack
direction='row'
justifyContent='space-between'
alignItems='center'
sx={{ mb: 2 }}
>
<Box sx={{ fontWeight: 700 }}></Box>
<Box sx={{ fontWeight: 700, lineHeight: '36px' }}></Box>
</Stack>
<Table
columns={columns}

View File

@@ -34,7 +34,7 @@ const PieCharts: React.FC<IPieChartsProps> = ({ title, data, extra }) => {
},
},
dataset: {
source: data.slice(0, 6),
source: data.slice(0, 5),
},
series: [
{

View File

@@ -116,17 +116,15 @@ const ModelItem = ({
}
sx={{ fontSize: 24 }}
/>
<Stack direction='row' alignItems='center' gap={1}>
{data.show_name && (
<Box sx={{ fontSize: 14, color: 'text.tertiary' }}>
{data.show_name} /
</Box>
)}
<Box sx={{ fontWeight: 700 }}>{data.model_name}</Box>
<Stack direction='row' alignItems='center' gap={1} sx={{ fontSize: 14, minWidth: 0 }}>
<Box sx={{ fontWeight: 700, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{data.show_name || '未命名'}
</Box>
<Box sx={{ color: 'text.tertiary', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
/ {data.model_name}
</Box>
</Stack>
</Stack>
{data.is_active && <StyledLabel color='success'>使</StyledLabel>}
</Stack>
<Stack
gap={1}
@@ -157,18 +155,21 @@ const ModelItem = ({
</Stack>
</Stack>
<Stack
direction='row-reverse'
direction='row'
justifyContent='space-between'
alignItems='center'
gap={2}
sx={{ mt: 2 }}
>
<Stack direction='row' alignItems='center'>
{data.is_active && <StyledLabel color='success'>使</StyledLabel>}
</Stack>
<Stack
direction='row'
justifySelf='flex-end'
sx={{ button: { minWidth: 0 } }}
gap={2}
>
<ButtonBase
{!data.is_internal && <ButtonBase
disableRipple
sx={{
color: 'info.main',
@@ -176,7 +177,7 @@ const ModelItem = ({
onClick={() => onEdit(data)}
>
</ButtonBase>
</ButtonBase>}
{!data.is_active && (
<ButtonBase
disableRipple
@@ -244,7 +245,10 @@ const ModelCard: React.FC<IModelCardProps> = ({ title, modelType }) => {
{modelList?.length > 0 ? (
<Grid container spacing={2} sx={{ mt: 2 }}>
{modelList.map((item) => (
<Grid size={4} key={item.id}>
<Grid
size={{ xs: 12, sm: 12, md: 12, lg: 6, xl: 4 }}
key={item.id}
>
<ModelItem data={item} onEdit={onEdit} refresh={refresh} />
</Grid>
))}

View File

@@ -25,7 +25,7 @@ const StyledCard = styled(Card)({
const StyledLabel = styled('div')(({ theme }) => ({
fontWeight: 700,
fontSize: 14,
fontSize: 16,
color: theme.vars.palette.text.primary,
}));
@@ -70,7 +70,7 @@ const User = () => {
<Grid size={6} container sx={{ height: '100%' }}>
<Stack gap={2} sx={{ height: '100%' }}>
<StyledCard>
<StyledLabel></StyledLabel>
<StyledLabel></StyledLabel>
<Switch
checked={data?.force_two_factor_auth}
onChange={(e) => {
@@ -79,7 +79,7 @@ const User = () => {
/>
</StyledCard>
<StyledCard>
<StyledLabel>使</StyledLabel>
<StyledLabel>使</StyledLabel>
<Switch
checked={data?.disable_password_login}
onChange={(e) =>
@@ -87,6 +87,15 @@ const User = () => {
}
/>
</StyledCard>
<StyledCard>
<StyledLabel></StyledLabel>
<Switch
checked={data?.enable_auto_login}
onChange={(e) =>
updateSetting({ enable_auto_login: e.target.checked })
}
/>
</StyledCard>
<StyledCard>
<StyledLabel>
@@ -110,15 +119,6 @@ const User = () => {
</Button>
</StyledCard>
<StyledCard>
<StyledLabel></StyledLabel>
<Switch
checked={data?.enable_auto_login}
onChange={(e) =>
updateSetting({ enable_auto_login: e.target.checked })
}
/>
</StyledCard>
<LoginHistory />
</Stack>
</Grid>