diff --git a/ui/README.md b/ui/README.md index 0ba2a29..4b65718 100644 --- a/ui/README.md +++ b/ui/README.md @@ -1,68 +1,226 @@ -# baizhiyun-app-airport +# Monkey Code UI -一个基于React 19和TypeScript的前端应用,使用Vite构建,Material UI作为UI框架。 +> 一个现代化的代码开发平台前端应用,基于 React 19 和 TypeScript 构建,提供智能代码统计、用户管理、AI模型管理等核心功能。 -## 技术栈 +[![React](https://img.shields.io/badge/React-19.1.0-blue.svg)](https://reactjs.org/) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.8.3-blue.svg)](https://www.typescriptlang.org/) +[![Vite](https://img.shields.io/badge/Vite-6.3.5-646CFF.svg)](https://vitejs.dev/) +[![Material-UI](https://img.shields.io/badge/Material--UI-6.4.12-0081CB.svg)](https://mui.com/) -- React 19 -- TypeScript -- Vite -- Material UI (@mui) + Emotion -- 状态管理: ahooks -- 路由: react-router-dom v7 -- 表单: react-hook-form -- 图表: echarts -- 动画: lottie-react -- API请求: axios -- 日期处理: dayjs -- Markdown处理: react-markdown +## ✨ 功能特性 -## 主要功能 +### 📊 数据统计 +- **全局统计**: 系统整体数据分析和可视化展示 +- **成员统计**: 用户个人数据统计和活动分析 +- **多维度图表**: 柱状图、折线图、饼图等多种数据展示方式 -1. **仪表盘**: 应用主界面,展示关键指标和数据概览 -2. **聊天功能**: 用户交流界面 -3. **完成/结果**: 展示操作结果或完成状态 -4. **模型管理**: 模型相关操作和展示 -5. **用户管理**: 用户信息管理 -6. **管理员功能**: 系统管理后台 -7. **认证和登录**: 用户认证流程 -8. **邀请系统**: 用户邀请功能 +### 💬 智能交互 +- **聊天功能**: 实时消息交流和会话管理 +- **完成状态**: 任务完成情况跟踪和结果展示 -## 项目结构 +### 🤖 模型管理 +- **模型配置**: AI模型参数设置和管理 +- **使用统计**: 模型调用次数和Token使用量统计 +- **性能监控**: 模型响应时间和成功率监控 + +### 👥 用户管理 +- **用户注册**: 完整的用户注册和认证流程 +- **权限管理**: 基于角色的访问控制(RBAC) +- **邀请系统**: 用户邀请和团队管理 +- **登录历史**: 用户登录记录和安全审计 + +### 🛡️ 管理后台 +- **系统管理**: 全面的系统配置和监控 +- **用户管理**: 管理员用户操作和权限分配 +- **数据分析**: 深度业务数据分析和报表 + +## 🛠️ 技术栈 + +### 前端框架 +- **React 19** - 最新的React框架,支持Concurrent Features +- **TypeScript** - 类型安全的JavaScript超集 +- **Vite** - 现代化的前端构建工具 + +### UI框架与样式 +- **Material-UI (@mui)** - Google Material Design组件库 +- **Emotion** - CSS-in-JS样式库 +- **@c-x/ui** - 自定义组件库 + +### 状态管理与数据 +- **ahooks** - React Hooks工具库 +- **react-hook-form** - 高性能表单管理 +- **axios** - HTTP客户端库 + +### 路由与导航 +- **react-router-dom v7** - 声明式路由管理 + +### 数据可视化 +- **ECharts** - 功能强大的图表库 +- **react-activity-calendar** - 活动日历组件 + +### 工具库 +- **dayjs** - 轻量级日期处理库 +- **decimal.js** - 精确数值计算 +- **lottie-react** - 动画效果库 +- **react-markdown** - Markdown渲染器 +- **react-syntax-highlighter** - 代码高亮显示 + +## 📁 项目结构 ``` -src/ -├── api/ # API请求相关 -├── assets/ # 静态资源 -├── components/ # 公共组件 -├── layouts/ # 布局组件 -├── pages/ # 页面组件 -│ ├── admin/ # 管理员相关 -│ ├── auth/ # 认证相关 -│ ├── chat/ # 聊天功能 -│ ├── completion/ # 完成/结果页面 -│ ├── dashboard/ # 仪表盘 -│ ├── invite/ # 邀请相关 -│ ├── login/ # 登录页面 -│ ├── model/ # 模型相关 -│ └── user/ # 用户相关 -├── router.tsx # 路由配置 -├── theme.ts # 主题配置 -├── utils/ # 工具函数 -└── main.tsx # 应用入口 +ui/ +├── src/ +│ ├── api/ # API请求层 +│ │ ├── Billing.ts # 计费相关API +│ │ ├── Dashboard.ts # 仪表盘API +│ │ ├── Model.ts # 模型管理API +│ │ ├── OpenAiv1.ts # OpenAI接口 +│ │ ├── User.ts # 用户管理API +│ │ ├── httpClient.ts # HTTP客户端配置 +│ │ └── types.ts # API类型定义 +│ ├── assets/ # 静态资源 +│ │ ├── fonts/ # 字体文件 +│ │ ├── images/ # 图片资源 +│ │ ├── json/ # JSON配置文件 +│ │ └── styles/ # 全局样式 +│ ├── components/ # 公共组件 +│ │ ├── avatar/ # 头像组件 +│ │ ├── card/ # 卡片组件 +│ │ ├── form/ # 表单组件 +│ │ ├── header/ # 头部组件 +│ │ ├── lottieIcon/ # 动画图标 +│ │ ├── markDown/ # Markdown渲染 +│ │ └── sidebar/ # 侧边栏组件 +│ ├── layouts/ # 布局组件 +│ │ └── mainLayout/ # 主布局 +│ ├── pages/ # 页面组件 +│ │ ├── admin/ # 管理员页面 +│ │ ├── auth/ # 认证页面 +│ │ ├── chat/ # 聊天页面 +│ │ ├── completion/ # 完成页面 +│ │ ├── dashboard/ # 仪表盘页面 +│ │ ├── expectation/ # 期望页面 +│ │ ├── invite/ # 邀请页面 +│ │ ├── login/ # 登录页面 +│ │ ├── model/ # 模型管理页面 +│ │ └── user/ # 用户管理页面 +│ ├── utils/ # 工具函数 +│ ├── router.tsx # 路由配置 +│ ├── theme.ts # 主题配置 +│ └── main.tsx # 应用入口 +├── public/ # 公共资源 +├── api-templates/ # API模板 +├── scripts/ # 构建脚本 +├── vite.config.ts # Vite配置 +├── tsconfig.json # TypeScript配置 +├── eslint.config.js # ESLint配置 +└── package.json # 项目依赖 ``` -## 开发环境配置 +## 🚀 快速开始 -1. 确保已安装Node.js (>=18.0.0) 和 pnpm -2. 克隆项目仓库 -3. 安装依赖: `pnpm install` -4. 启动开发服务器: `pnpm dev` -5. 打开浏览器访问: `http://localhost:5173` +### 环境要求 -## 常用命令 +- **Node.js** >= 18.0.0 +- **pnpm** >= 7.0.0 (推荐使用pnpm作为包管理器) -- `pnpm dev`: 启动开发服务器 -- `pnpm build`: 生产环境构建 -- `pnpm preview`: 预览生产构建 -- `pnpm lint`: 运行代码检查 +### 安装步骤 + +1. **克隆项目** +```bash +git clone +cd monkey-code-ui +``` + +2. **安装依赖** +```bash +pnpm install +``` + +3. **启动开发服务器** +```bash +pnpm dev +``` + +4. **访问应用** +打开浏览器访问: `http://localhost:3300` + +### 环境配置 + +创建 `.env` 文件配置环境变量: + +```bash +# API服务地址 +VITE_API_BASE_URL=http://localhost:8080/ + +# 其他环境变量... +``` + +## 📝 可用脚本 + +| 命令 | 说明 | +|------|------| +| `pnpm dev` | 启动开发服务器 (端口: 3300) | +| `pnpm build` | 构建生产版本 | +| `pnpm preview` | 预览生产构建结果 | +| `pnpm lint` | 运行ESLint代码检查 | +| `pnpm api` | 生成API接口代码 | +| `pnpm icon` | 下载图标资源 | + +## 🏗️ 构建部署 + +### 生产构建 + +```bash +pnpm build +``` + +构建产物将生成在 `dist/` 目录中。 + +### Docker 部署 + +项目包含 `nginx.conf` 配置文件,可用于 Docker 容器化部署: + +```bash +# 构建Docker镜像 +docker build -t monkey-code-ui . + +# 运行容器 +docker run -p 80:80 monkey-code-ui +``` + +## 🔧 开发指南 + +### 代码规范 + +- 使用 **TypeScript** 进行类型检查 +- 遵循 **ESLint** 代码规范 +- 使用 **Prettier** 格式化代码 +- 采用 **函数式组件** + **Hooks** 开发模式 + +### API 集成 + +项目使用统一的API客户端配置,支持: +- 请求/响应拦截器 +- 错误处理 +- 类型安全的接口定义 + +### 主题定制 + +在 `src/theme.ts` 中配置Material-UI主题: +- 颜色方案 +- 字体设置 +- 组件样式覆盖 + +## 🤝 贡献指南 + +1. Fork 项目 +2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 开启 Pull Request + + +## 📞 联系方式 + +如有问题或建议,请联系开发团队。 diff --git a/ui/package.json b/ui/package.json index 67af1c7..26a74a7 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,5 @@ { - "name": "baizhiyun-app-airport", - "private": true, + "name": "monkey-code-ui", "version": "0.0.0", "type": "module", "scripts": { diff --git a/ui/public/logo.png b/ui/public/logo.png index b284ef8..4e426f5 100644 Binary files a/ui/public/logo.png and b/ui/public/logo.png differ diff --git a/ui/src/assets/fonts/font.css b/ui/src/assets/fonts/font.css new file mode 100644 index 0000000..9d3b0a1 --- /dev/null +++ b/ui/src/assets/fonts/font.css @@ -0,0 +1,20 @@ +@font-face { + font-family: 'gilroy'; + src: url('./gilroy-bold.otf'); + font-weight: 700; + font-style: normal; +} + +@font-face { + font-family: 'gilroy'; + src: url('./gilroy-medium.otf'); + font-weight: 500; + font-style: normal; +} + +@font-face { + font-family: 'gilroy'; + src: url('./gilroy-regular.otf'); + font-weight: 400; + font-style: normal; +} diff --git a/ui/src/assets/fonts/gilroy-bold.otf b/ui/src/assets/fonts/gilroy-bold.otf new file mode 100755 index 0000000..b89563f Binary files /dev/null and b/ui/src/assets/fonts/gilroy-bold.otf differ diff --git a/ui/src/assets/fonts/gilroy-medium.otf b/ui/src/assets/fonts/gilroy-medium.otf new file mode 100755 index 0000000..7f03b58 Binary files /dev/null and b/ui/src/assets/fonts/gilroy-medium.otf differ diff --git a/ui/src/assets/fonts/gilroy-regular.otf b/ui/src/assets/fonts/gilroy-regular.otf new file mode 100755 index 0000000..73ed9d5 Binary files /dev/null and b/ui/src/assets/fonts/gilroy-regular.otf differ diff --git a/ui/src/assets/images/logo.png b/ui/src/assets/images/logo.png index 78ff535..fd35eb9 100644 Binary files a/ui/src/assets/images/logo.png and b/ui/src/assets/images/logo.png differ diff --git a/ui/src/assets/react.svg b/ui/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/ui/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/src/components/label/index.tsx b/ui/src/components/label/index.tsx new file mode 100644 index 0000000..3db9b89 --- /dev/null +++ b/ui/src/components/label/index.tsx @@ -0,0 +1,57 @@ +import { styled, alpha } from '@mui/material'; + +interface StyledLabelProps { + color?: 'default' | 'success' | 'warning' | 'error' | 'info' | string; +} + +const StyledLabel = styled('div')( + ({ theme, color = 'default' }) => { + // 获取颜色值 + const getColor = (colorProp: string) => { + // 如果是主题预设颜色 + if (['success', 'warning', 'error', 'info'].includes(colorProp)) { + return theme.palette[ + colorProp as 'success' | 'warning' | 'error' | 'info' + ].main; + } + // 如果是 default,使用灰色 + if (colorProp === 'default') { + return theme.palette.grey[500]; + } + // 如果是自定义颜色字符串 + return colorProp; + }; + + const textColor = getColor(color); + + // 获取背景颜色(淡化版本) + const getBackgroundColor = (colorProp: string) => { + if (['success', 'warning', 'error', 'info'].includes(colorProp)) { + // 使用主题的 light 版本,如果没有则使用 alpha 透明度 + const palette = + theme.palette[colorProp as 'success' | 'warning' | 'error' | 'info']; + return alpha(palette.main, 0.15); + } + // 如果是 default,使用淡灰色背景 + if (colorProp === 'default') { + return theme.palette.grey[100]; + } + // 对于自定义颜色,使用低透明度作为背景 + return alpha(colorProp, 0.15); + }; + + return { + padding: theme.spacing(0.5, 1), + borderRadius: theme.shape.borderRadius * 4, + color: textColor, + backgroundColor: getBackgroundColor(color), + border: `1px solid ${alpha(textColor, 0.3)}`, + display: 'inline-flex', + alignItems: 'center', + fontSize: theme.typography.caption.fontSize, + fontWeight: theme.typography.fontWeightMedium, + }; + } +); + +export default StyledLabel; diff --git a/ui/src/components/sidebar/index.tsx b/ui/src/components/sidebar/index.tsx index 7e91bf7..f3973ce 100644 --- a/ui/src/components/sidebar/index.tsx +++ b/ui/src/components/sidebar/index.tsx @@ -92,15 +92,18 @@ const Sidebar = () => { window.open('https://baizhi.cloud', '_blank'); }} > - + - MonkeyCode + Monkey Code diff --git a/ui/src/index.css b/ui/src/index.css index 18d4fb2..bbcc1fb 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -11,9 +11,14 @@ html { padding: 0; } +:root { + --font-gilory: 'gilroy'; + --font-HarmonyOS: 'HarmonyOS'; +} + body { margin: 0; - font-family: 'PingFang SC', var(--font-gilory), var(--font-HarmonyOS), + font-family: var(--font-gilory), var(--font-HarmonyOS), 'PingFang SC', 'Roboto', 'Helvetica', 'Arial', sans-serif; color: var(--mui-palette-text-primary); font-weight: 400; diff --git a/ui/src/main.tsx b/ui/src/main.tsx index a5b192c..154572e 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -1,6 +1,7 @@ import ReactDOM from 'react-dom/client'; import 'dayjs/locale/zh-cn'; import { RouterProvider } from 'react-router-dom'; +import '@/assets/fonts/font.css'; import '@/assets/fonts/iconfont'; import './index.css'; import '@/assets/styles/markdown.css'; diff --git a/ui/src/pages/admin/adminTable.tsx b/ui/src/pages/admin/adminTable.tsx index 9adb3a4..4d0bfe0 100644 --- a/ui/src/pages/admin/adminTable.tsx +++ b/ui/src/pages/admin/adminTable.tsx @@ -226,6 +226,7 @@ const AdminTable = () => { + // height='calc(100% - 50px)' columns={columns} loading={loading} dataSource={data?.users || []} diff --git a/ui/src/pages/admin/index.tsx b/ui/src/pages/admin/index.tsx index b4e7a44..e0a5324 100644 --- a/ui/src/pages/admin/index.tsx +++ b/ui/src/pages/admin/index.tsx @@ -5,7 +5,7 @@ import { Stack } from '@mui/material'; const Admin = () => { return ( - + diff --git a/ui/src/pages/chat/index.tsx b/ui/src/pages/chat/index.tsx index 62b5da7..4378c26 100644 --- a/ui/src/pages/chat/index.tsx +++ b/ui/src/pages/chat/index.tsx @@ -4,8 +4,10 @@ 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 StyledLabel from '@/components/label'; import ChatDetailModal from './chatDetailModal'; import { ColumnsType } from '@c-x/ui/dist/Table'; @@ -69,19 +71,15 @@ const Chat = () => { title: '工作模式', width: 120, render(value: DomainChatRecord['work_mode']) { - const workModeMap = { + const workModeMap: Record = { code: 'warning', chat: 'success', ask: 'info', }; return ( - + + {value} + ); }, }, diff --git a/ui/src/pages/completion/index.tsx b/ui/src/pages/completion/index.tsx index d07a320..dadd669 100644 --- a/ui/src/pages/completion/index.tsx +++ b/ui/src/pages/completion/index.tsx @@ -9,6 +9,7 @@ import dayjs from 'dayjs'; import { ColumnsType } from '@c-x/ui/dist/Table'; import { addCommasToNumber } from '@/utils'; import CompletionDetailModal from './completionDetailModal'; +import StyledLabel from '@/components/label'; const Completion = () => { const [page, setPage] = useState(1); @@ -65,12 +66,9 @@ const Completion = () => { title: '是否采纳', width: 130, render(value: boolean) { + const color = value ? 'success' : 'default'; return ( - + {value ? '已采纳' : '未采纳'} ); }, }, diff --git a/ui/src/pages/dashboard/components/globalStatistic.tsx b/ui/src/pages/dashboard/components/globalStatistic.tsx index 57a0cb3..e2a4be4 100644 --- a/ui/src/pages/dashboard/components/globalStatistic.tsx +++ b/ui/src/pages/dashboard/components/globalStatistic.tsx @@ -87,110 +87,110 @@ const GlobalStatistic = () => { borderRadius: 2.5, }} > - - - - - - - 最近 90 天共 - - {timeStatData?.total_users || 0} - - 个活跃用户数 - - } - /> - - - - - - - - - - - - - 最近 90 天共 - - {timeStatData?.total_chats || 0} - - 个对话任务 - - } - /> - - - - 最近 90 天共 - - {timeStatData?.total_completions || 0} - - 个补全任务 - - } - /> - - - - 最近 90 天共修改 - - {timeStatData?.total_lines_of_code || 0} - - 行代码 - - } - /> - - - - 最近 90 天平均采纳率为 - - {(timeStatData?.total_accepted_per || 0).toFixed(2)} - - % - - } - /> - + {/* */} + + + + + + 最近 90 天共 + + {timeStatData?.total_users || 0} + + 个活跃用户数 + + } + /> + + + + + + + + + + + + 最近 90 天共 + + {timeStatData?.total_chats || 0} + + 个对话任务 + + } + /> + + + + 最近 90 天共 + + {timeStatData?.total_completions || 0} + + 个补全任务 + + } + /> + + + + 最近 90 天共修改 + + {timeStatData?.total_lines_of_code || 0} + + 行代码 + + } + /> + + + + 最近 90 天平均采纳率为 + + {(timeStatData?.total_accepted_per || 0).toFixed(2)} + + % + + } + /> + + {/* */} ); }; diff --git a/ui/src/pages/dashboard/components/statisticCard.tsx b/ui/src/pages/dashboard/components/statisticCard.tsx index 488d005..de6f3c2 100644 --- a/ui/src/pages/dashboard/components/statisticCard.tsx +++ b/ui/src/pages/dashboard/components/statisticCard.tsx @@ -30,7 +30,7 @@ const StyledItem = styled('div')(({ theme }) => ({ const StyledText = styled('a')(({ theme }) => ({ fontSize: 14, - color: theme.palette.text.secondary, + color: theme.palette.text.primary, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', @@ -47,6 +47,7 @@ const StyledSerialNumber = styled('span')<{ num: number }>(({ theme, num }) => { color: color, fontSize: 14, fontWeight: 700, + width: 8, }; }); @@ -64,8 +65,16 @@ export const ContributionCard = ({ 最近 90 天 - - {data.map((item, index) => ( + + {data.slice(0, 5).map((item, index) => ( {item.lines} ))} - + ); }; diff --git a/ui/src/pages/model/components/modelCard.tsx b/ui/src/pages/model/components/modelCard.tsx index 5a40370..a719770 100644 --- a/ui/src/pages/model/components/modelCard.tsx +++ b/ui/src/pages/model/components/modelCard.tsx @@ -3,7 +3,8 @@ import Card from '@/components/card'; import { useRequest } from 'ahooks'; import { getMyModelList, putUpdateModel } from '@/api/Model'; import { DomainModel, ConstsModelStatus } from '@/api/types'; -import { Stack, Box, Button, Grid2 as Grid, Chip } from '@mui/material'; +import { Stack, Box, Button, Grid2 as Grid, ButtonBase } from '@mui/material'; +import StyledLabel from '@/components/label'; import { Icon, Modal, message } from '@c-x/ui'; import { addCommasToNumber } from '@/utils'; import NoData from '@/assets/images/nodata.png'; @@ -78,26 +79,31 @@ const ModelItem = ({ position: 'relative', transition: 'all 0.3s ease', boxShadow: - '0px 12px 24px -4px rgba(68, 80, 91, 0.1), 0px 0px 2px 0px rgba(68, 80, 91, 0.1)', + '0px 0px 10px 0px rgba(68, 80, 91, 0.1), 0px 0px 2px 0px rgba(68, 80, 91, 0.1)', '&:hover': { boxShadow: 'rgba(54, 59, 76, 0.3) 0px 10px 30px 0px, rgba(54, 59, 76, 0.03) 0px 0px 1px 1px', }, - '&::before': { - content: '""', - position: 'absolute', - top: 0, - left: 0, - right: 0, - height: 3, - background: data.is_active - ? 'linear-gradient(90deg, #4CAF50, #66BB6A)' - : 'linear-gradient(90deg, #E0E0E0, #BDBDBD)', - transition: 'all 0.3s ease', - }, + // '&::before': { + // content: '""', + // position: 'absolute', + // top: 0, + // left: 0, + // right: 0, + // height: 3, + // background: data.is_active + // ? 'linear-gradient(90deg, #4CAF50, #66BB6A)' + // : 'linear-gradient(90deg, #E0E0E0, #BDBDBD)', + // transition: 'all 0.3s ease', + // }, }} > - + {data.model_name} - {data.is_active && ( - - )} + {data.is_active && 正在使用} - + {!data.is_active && ( - + )} {data.is_active && ( - + )} {/*