diff --git a/ui/src/components/avatar/index.tsx b/ui/src/components/avatar/index.tsx
index 3022104..276dd2d 100644
--- a/ui/src/components/avatar/index.tsx
+++ b/ui/src/components/avatar/index.tsx
@@ -1,6 +1,5 @@
import Logo from '@/assets/images/logo.png';
import { Avatar as MuiAvatar, type SxProps } from '@mui/material';
-import { Icon } from '@c-x/ui';
import { type ReactNode } from 'react';
interface AvatarProps {
@@ -41,21 +40,15 @@ function stringAvatar(name: string) {
const Avatar = (props: AvatarProps) => {
const src = props.src;
+ const avatarObj = props.name ? stringAvatar(props.name) : undefined;
return (
,
- })}
+ ? { children: avatarObj!.children }
+ : { children:
})}
src={src}
- >
+ />
);
};
diff --git a/ui/src/components/markDown/code.tsx b/ui/src/components/markDown/code.tsx
new file mode 100644
index 0000000..b1f1987
--- /dev/null
+++ b/ui/src/components/markDown/code.tsx
@@ -0,0 +1,60 @@
+// @ts-nocheck
+import React from 'react';
+
+const Code = () => {
+ return (
+
+ {
+ editorRef.current = editor;
+ setEditorReady(true);
+ // 隐藏光标
+ const editorDom = editor.getDomNode();
+ if (editorDom) {
+ const style = document.createElement('style');
+ style.innerHTML = `.monaco-editor .cursor { display: none !important; }`;
+ editorDom.appendChild(style);
+ }
+ }}
+ />
+
+ );
+};
+
+export default Code;
diff --git a/ui/src/components/user/index.tsx b/ui/src/components/user/index.tsx
new file mode 100644
index 0000000..ed0e1cb
--- /dev/null
+++ b/ui/src/components/user/index.tsx
@@ -0,0 +1,43 @@
+import { Stack, Link, Typography } from '@mui/material';
+import { Link as RouterLink } from 'react-router-dom';
+import Avatar from '../avatar';
+
+const User = ({
+ id,
+ username = '',
+ email = '',
+}: {
+ id?: string;
+ username?: string;
+ email?: string;
+}) => {
+ return (
+
+
+
+
+ {username}
+
+
+ {email && (
+
+ {email}
+
+ )}
+
+ );
+};
+
+export default User;
diff --git a/ui/src/pages/admin/adminTable.tsx b/ui/src/pages/admin/adminTable.tsx
index 4d0bfe0..d083fc2 100644
--- a/ui/src/pages/admin/adminTable.tsx
+++ b/ui/src/pages/admin/adminTable.tsx
@@ -17,7 +17,7 @@ import dayjs from 'dayjs';
import { DomainAdminUser } from '@/api/types';
import { Controller, useForm } from 'react-hook-form';
import { useEffect, useState } from 'react';
-import ContentCopyIcon from '@mui/icons-material/ContentCopy';
+import User from '@/components/user';
const AddAdminModal = ({
open,
@@ -182,6 +182,9 @@ const AdminTable = () => {
{
title: '账号',
dataIndex: 'username',
+ render: (text) => {
+ return ;
+ },
},
{
title: '最近活跃时间',
diff --git a/ui/src/pages/admin/loginHistory.tsx b/ui/src/pages/admin/loginHistory.tsx
index 43460ae..1018254 100644
--- a/ui/src/pages/admin/loginHistory.tsx
+++ b/ui/src/pages/admin/loginHistory.tsx
@@ -5,7 +5,8 @@ import dayjs from 'dayjs';
import { useRequest } from 'ahooks';
import { getAdminLoginHistory } from '@/api/User';
import { ColumnsType } from '@c-x/ui/dist/Table';
-import { DomainListAdminLoginHistoryResp, DomainAdminUser } from '@/api/types';
+import { DomainListAdminLoginHistoryResp } from '@/api/types';
+import User from '@/components/user';
type LoginHistory = NonNullable<
DomainListAdminLoginHistoryResp['login_histories']
@@ -18,7 +19,7 @@ const LoginHistory = () => {
title: '账号',
dataIndex: 'user',
render: (user, record) => {
- return record?.user?.username;
+ return ;
},
},
{
diff --git a/ui/src/pages/chat/index.tsx b/ui/src/pages/chat/index.tsx
index e7c5a30..b1cca2f 100644
--- a/ui/src/pages/chat/index.tsx
+++ b/ui/src/pages/chat/index.tsx
@@ -1,23 +1,17 @@
import React, { useState, useEffect } from 'react';
-import { Table, Ellipsis } from '@c-x/ui';
+import { Table } from '@c-x/ui';
import { getListChatRecord } from '@/api/Billing';
-import { aggregatedTime } from '@/utils';
import dayjs from 'dayjs';
-import { convertTokensToRMB } from '@/utils';
import Card from '@/components/card';
-import { Box, Stack, styled, Chip } from '@mui/material';
+import { Box } from '@mui/material';
import StyledLabel from '@/components/label';
import ChatDetailModal from './chatDetailModal';
import { ColumnsType } from '@c-x/ui/dist/Table';
-import { DomainChatRecord } from '@/api/types';
+import { DomainChatRecord, DomainUser } from '@/api/types';
import { addCommasToNumber } from '@/utils';
-
-const StyledHighlightText = styled('span')(({ theme }) => ({
- color: theme.vars.palette.text.primary,
- fontWeight: 700,
-}));
+import User from '@/components/user';
const Chat = () => {
const [page, setPage] = useState(1);
@@ -47,9 +41,15 @@ const Chat = () => {
{
dataIndex: 'user',
title: '成员',
- width: 160,
- render(value: DomainChatRecord['user']) {
- return value?.username;
+ width: 260,
+ render(value: DomainUser) {
+ return (
+
+ );
},
},
{
diff --git a/ui/src/pages/completion/index.tsx b/ui/src/pages/completion/index.tsx
index f6e40c0..bd87d04 100644
--- a/ui/src/pages/completion/index.tsx
+++ b/ui/src/pages/completion/index.tsx
@@ -1,29 +1,18 @@
-import React, {
- useState,
- useEffect,
- useMemo,
- useCallback,
- useRef,
-} from 'react';
-import { DomainCompletionRecord } from '@/api/types';
+import { useState, useEffect, useCallback, useRef } from 'react';
+import { DomainCompletionRecord, DomainUser } from '@/api/types';
import { getListCompletionRecord } from '@/api/Billing';
import { useRequest } from 'ahooks';
import { Table } from '@c-x/ui';
import Card from '@/components/card';
import {
- Box,
- Button,
ButtonBase,
- Chip,
Stack,
- alpha,
MenuItem,
Select,
FormControl,
InputLabel,
Autocomplete,
TextField,
- InputAdornment,
} from '@mui/material';
import { getListUser } from '@/api/User';
import dayjs from 'dayjs';
@@ -33,6 +22,7 @@ import CompletionDetailModal from './completionDetailModal';
import StyledLabel from '@/components/label';
import { LANG_OPTIONS } from './constant';
import { ArrowDropDown as ArrowDropDownIcon } from '@mui/icons-material';
+import User from '@/components/user';
// 防抖 hook
function useDebounce(fn: (...args: any[]) => void, delay: number) {
@@ -113,8 +103,14 @@ const Completion = () => {
{
dataIndex: 'user',
title: '成员',
- render(value: DomainCompletionRecord['user']) {
- return value?.username;
+ render(value: DomainUser) {
+ return (
+
+ );
},
},
{
diff --git a/ui/src/pages/dashboard/components/contributionModal.tsx b/ui/src/pages/dashboard/components/contributionModal.tsx
new file mode 100644
index 0000000..055b69c
--- /dev/null
+++ b/ui/src/pages/dashboard/components/contributionModal.tsx
@@ -0,0 +1,64 @@
+import { Box, Stack } from '@mui/material';
+import { DomainUserCodeRank } from '@/api/types';
+import { Modal } from '@c-x/ui';
+import { StyledItem, StyledSerialNumber, StyledText } from './statisticCard';
+
+const ContributionModal = ({
+ open,
+ onCancel,
+ data,
+}: {
+ open: boolean;
+ onCancel: () => void;
+ data: DomainUserCodeRank[];
+}) => {
+ return (
+
+
+ {data.map((item, index) => (
+
+
+
+ {index + 1}
+
+
+ {/* */}
+
+ {item.username}
+
+
+
+ {item.lines}
+
+ ))}
+
+
+ );
+};
+
+export default ContributionModal;
diff --git a/ui/src/pages/dashboard/components/statisticCard.tsx b/ui/src/pages/dashboard/components/statisticCard.tsx
index 7d5008a..5c64809 100644
--- a/ui/src/pages/dashboard/components/statisticCard.tsx
+++ b/ui/src/pages/dashboard/components/statisticCard.tsx
@@ -1,9 +1,10 @@
-import React from 'react';
-import { styled, Stack, Box } from '@mui/material';
+import React, { useState } from 'react';
+import { styled, Stack, Box, Button } from '@mui/material';
import { Empty } from '@c-x/ui';
import dayjs from 'dayjs';
import { useNavigate } from 'react-router-dom';
import { TimeRange } from '../index';
+import ContributionModal from './contributionModal';
import Card from '@/components/card';
import {
@@ -23,13 +24,13 @@ const StyledCardValue = styled('div')(({ theme }) => ({
fontWeight: 700,
}));
-const StyledItem = styled('div')(({ theme }) => ({
+export const StyledItem = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(2),
}));
-const StyledText = styled('a')(({ theme }) => ({
+export const StyledText = styled('a')(({ theme }) => ({
fontSize: 14,
color: theme.palette.text.primary,
overflow: 'hidden',
@@ -37,20 +38,22 @@ const StyledText = styled('a')(({ theme }) => ({
whiteSpace: 'nowrap',
}));
-const StyledSerialNumber = styled('span')<{ num: number }>(({ theme, num }) => {
- const numToColor = {
- 1: '#FE4545',
- 2: '#FF6600',
- 3: '#FFC600',
- };
- const color = numToColor[num as 1] || '#BCBCBC';
- return {
- color: color,
- fontSize: 14,
- fontWeight: 700,
- width: 8,
- };
-});
+export const StyledSerialNumber = styled('span')<{ num: number }>(
+ ({ theme, num }) => {
+ const numToColor = {
+ 1: '#FE4545',
+ 2: '#FF6600',
+ 3: '#FFC600',
+ };
+ const color = numToColor[num as 1] || '#BCBCBC';
+ return {
+ color: color,
+ fontSize: 14,
+ fontWeight: 700,
+ width: 8,
+ };
+ }
+);
export const ContributionCard = ({
data = [],
@@ -60,11 +63,34 @@ export const ContributionCard = ({
timeRange: TimeRange;
}) => {
const navigate = useNavigate();
-
+ const [contributionModalOpen, setContributionModalOpen] = useState(false);
return (
+ setContributionModalOpen(false)}
+ data={data}
+ />
- 用户贡献榜
+
+ 用户贡献榜
+ setContributionModalOpen(true)}
+ >
+ 查看更多
+
+
{timeRange === '90d' ? '最近 90 天' : '最近 24 小时'}
diff --git a/ui/src/pages/user/index.tsx b/ui/src/pages/user/index.tsx
index 2e2c011..edc8cf8 100644
--- a/ui/src/pages/user/index.tsx
+++ b/ui/src/pages/user/index.tsx
@@ -52,13 +52,13 @@ const User = () => {
});
return (
-
-
-
+
+
+
-
-
+
+
强制成员启用两步认证
{
}}
/>
-
-
禁止成员使用密码登录
{
}
/>
-
-
-
-
+
第三方登录
{
配置
-
-
-
-
+
diff --git a/ui/src/pages/user/loginHistory.tsx b/ui/src/pages/user/loginHistory.tsx
index 208dc85..ef67063 100644
--- a/ui/src/pages/user/loginHistory.tsx
+++ b/ui/src/pages/user/loginHistory.tsx
@@ -6,6 +6,7 @@ import { Table } from '@c-x/ui';
import dayjs from 'dayjs';
import { ColumnsType } from '@c-x/ui/dist/Table';
import { DomainListLoginHistoryResp } from '@/api/types';
+import User from '@/components/user';
type LoginHistory = NonNullable<
DomainListLoginHistoryResp['login_histories']
@@ -18,7 +19,13 @@ const LoginHistory = () => {
title: '账号',
dataIndex: 'user',
render: (user, record) => {
- return record?.user?.username;
+ return (
+
+ );
},
},
{
@@ -54,7 +61,7 @@ const LoginHistory = () => {
},
];
return (
-
+
{
登录历史
{
title: '账号',
dataIndex: 'username',
render: (text, record) => {
- return <>
-
- navigate(`/dashboard/member/${record.id}`)} sx={{
- '&:hover': {
- color: 'info.main',
- },
- cursor: 'pointer'
- }}>
-
-
- {text}
-
-
- {record.email}
-
- >;
+ return (
+
+ );
},
},
{
@@ -282,7 +274,9 @@ const MemberManage = () => {
dataIndex: 'last_active_at',
width: 120,
render: (text, record) => {
- return record.last_active_at === 0 ? '从未使用' : dayjs.unix(text).fromNow();
+ return record.last_active_at === 0
+ ? '从未使用'
+ : dayjs.unix(text).fromNow();
},
},
{
@@ -305,9 +299,8 @@ const MemberManage = () => {
},
},
];
- console.log(currentUser);
return (
-
+