# code-farmer-training-manual-front **Repository Path**: code-farmer-training-manual/code-farmer-training-manual-front ## Basic Information - **Project Name**: code-farmer-training-manual-front - **Description**: 前端 - **Primary Language**: HTML - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-10 - **Last Updated**: 2025-12-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 码农修炼手册 OJ - 前端项目 > **版本**: Vue 3.2+ / TypeScript 4.5+ > **最后更新**: 2025-10-28 --- ## 📋 目录 - [快速开始](#-快速开始) - [项目结构](#-项目结构) - [技术栈](#-技术栈) - [核心组件](#-核心组件) - [路由与权限](#-路由与权限) - [开发指南](#-开发指南) - [构建部署](#-构建部署) --- ## 🚀 快速开始 ### 环境要求 | 软件 | 版本要求 | |------|---------| | Node.js | 14.0+ | | npm | 6.0+ | ⚠️ **重要提示**:本项目仅支持 **npm**,不支持 yarn 或其他包管理器。 ### 安装依赖 ```bash # 进入项目目录 cd code-farmer-training-manual-front # 安装依赖(必须使用 npm) npm install ``` 如果 IDE 提示使用 yarn,请点击 **"Don't ask again"** 忽略该提示。 ### 启动开发服务器 ```bash npm run serve ``` 项目将运行在 http://localhost:8080 ### 生产构建 ```bash npm run build ``` 构建产物将输出到 `dist/` 目录 ### 代码检查 ```bash npm run lint ``` --- ## 📁 项目结构 ``` code-farmer-training-manual-front/ ├── public/ # 静态资源 │ ├── favicon.ico # 网站图标 │ └── logo.png # Logo 图片 │ ├── src/ │ ├── access/ # 权限控制模块 │ │ ├── accessEnum.ts # 权限枚举(NOT_LOGIN, USER, ADMIN) │ │ ├── checkAccess.ts # 权限检查工具函数 │ │ └── index.ts # 路由权限拦截器 │ │ │ ├── assets/ # 静态资源 │ │ ├── logo.png # Logo │ │ └── oj-logo.svg # OJ Logo │ │ │ ├── components/ # 公共组件 │ │ ├── CodeEditor.vue # Monaco 代码编辑器 │ │ ├── GlobalHeader.vue # 全局顶部导航 │ │ ├── MdEditor.vue # Markdown 编辑器 │ │ └── MdViewer.vue # Markdown 预览器 │ │ │ ├── layouts/ # 布局组件 │ │ ├── BasicLayout.vue # 基础布局(带导航栏) │ │ └── UserLayout.vue # 用户页面布局(登录/注册) │ │ │ ├── router/ # 路由配置 │ │ ├── index.ts # 路由实例,权限拦截 │ │ └── routes.ts # 路由表定义 │ │ │ ├── store/ # Vuex 状态管理 │ │ ├── index.ts # Store 入口 │ │ └── user.ts # 用户状态模块 │ │ │ ├── views/ # 页面视图 │ │ ├── question/ # 题目相关页面 │ │ │ ├── QuestionsView.vue # 浏览题目 │ │ │ ├── ViewQuestionView.vue # 在线做题 │ │ │ ├── AddQuestionView.vue # 创建/编辑题目 │ │ │ ├── ManageQuestionView.vue # 题目管理 │ │ │ └── QuestionSubmitView.vue # 提交记录 │ │ │ │ │ ├── contest/ # 竞赛相关页面 │ │ │ ├── ContestListView.vue # 竞赛列表 │ │ │ ├── ContestDetailView.vue # 竞赛详情 │ │ │ ├── CreateContestView.vue # 创建/编辑竞赛 │ │ │ └── ManageContestView.vue # 竞赛管理 │ │ │ │ │ ├── daily/ # 每日一题 │ │ │ └── DailyQuestionView.vue # 每日一题页面 │ │ │ │ │ ├── user/ # 用户相关页面 │ │ │ ├── UserLoginView.vue # 用户登录 │ │ │ └── UserRegisterView.vue # 用户注册 │ │ │ │ │ ├── NoAuthView.vue # 无权限提示页 │ │ └── AdminView.vue # 管理员页面 │ │ │ ├── config/ # 配置文件 │ │ └── api.ts # API 配置 │ │ │ ├── App.vue # 根组件 │ ├── main.ts # 应用入口 │ ├── shims-vue.d.ts # Vue 类型声明 │ └── vite-env.d.ts # Vite 环境变量类型 │ ├── generated/ # 自动生成的 API 代码 │ ├── core/ # 核心工具 │ │ ├── OpenAPI.ts # API 配置(BASE URL) │ │ └── request.ts # HTTP 请求封装(Fetch API) │ ├── models/ # 数据模型(TS 接口) │ └── services/ # API 服务 │ ├── UserControllerService.ts │ ├── QuestionControllerService.ts │ ├── QuestionSubmitControllerService.ts │ └── ... │ ├── config/ # 构建配置 ├── package.json # 项目依赖配置 ├── tsconfig.json # TypeScript 配置 ├── vite.config.ts # Vite 配置 └── README.md # 本文档 ``` --- ## 🛠️ 技术栈 ### 核心框架 | 技术 | 版本 | 说明 | |------|------|------| | Vue 3 | 3.2+ | 渐进式 JavaScript 框架,采用 Composition API | | TypeScript | 4.5+ | 类型安全的 JavaScript 超集 | | Vite | 3.x | 下一代前端构建工具,快速热更新 | ### 状态与路由 | 技术 | 版本 | 说明 | |------|------|------| | Vue Router | 4.0+ | 官方路由管理器 | | Vuex | 4.0+ | 集中式状态管理 | ### UI 组件库 | 技术 | 版本 | 说明 | |------|------|------| | Arco Design Vue | 2.49+ | 字节跳动企业级 UI 组件库 | | Monaco Editor | - | VS Code 同款代码编辑器 | | ByteMD | - | Markdown 编辑器和预览组件 | ### 开发工具 | 技术 | 版本 | 说明 | |------|------|------| | ESLint | 8.x | 代码质量检查 | | Prettier | 2.x | 代码格式化 | | TypeScript ESLint | 5.x | TypeScript 代码规范 | ### HTTP 通信 | 技术 | 说明 | |------|------| | Fetch API | 原生浏览器 API,无需额外依赖 | | OpenAPI TypeScript Codegen | 自动生成 API 客户端代码 | --- ## 📦 核心组件 ### 1. CodeEditor(代码编辑器) 基于 Monaco Editor 的代码编辑组件,提供专业的代码编辑体验。 **功能特性**: - ✅ 语法高亮 - ✅ 代码补全 - ✅ 代码折叠 - ✅ 多语言支持(Java、C++、Python、Go 等) - ✅ 主题切换(Light / Dark) **使用示例**: ```vue ``` ### 2. MdEditor / MdViewer(Markdown 编辑器/预览器) 支持 Markdown 格式的内容编辑和展示。 **功能特性**: - ✅ 实时预览 - ✅ 工具栏快捷操作 - ✅ 代码块语法高亮 - ✅ 支持表格、公式、图片 **使用示例**: ```vue ``` ### 3. GlobalHeader(全局导航栏) 顶部导航栏组件,包含路由导航和用户菜单。 **功能特性**: - ✅ 动态路由菜单 - ✅ 用户登录状态显示 - ✅ 权限控制(根据角色显示不同菜单) - ✅ 响应式布局 --- ## 🔐 路由与权限 ### 权限级别 系统实现了三级权限控制: | 权限级别 | 说明 | 可访问功能 | |----------|------|-----------| | `NOT_LOGIN` | 游客 | 浏览题目列表、查看提交记录 | | `USER` | 普通用户 | 在线做题、提交代码、参加竞赛、评论 | | `ADMIN` | 管理员 | 创建/编辑/删除题目、管理竞赛 | ### 路由配置 路由定义在 `src/router/routes.ts`: ```typescript { path: "/questions", name: "浏览题目", component: QuestionsView, // 无 meta.access 表示所有人可访问 } { path: "/view/question/:id", name: "在线做题", component: ViewQuestionView, meta: { access: ACCESS_ENUM.USER, // 需要登录 }, } { path: "/add/question", name: "创建题目", component: AddQuestionView, meta: { access: ACCESS_ENUM.ADMIN, // 需要管理员权限 hideInMenu: true, }, } ``` ### 权限拦截 路由守卫会在导航前检查权限: ```typescript router.beforeEach(async (to, from, next) => { // 1. 获取当前用户信息 const loginUser = store.state.user.loginUser; // 2. 检查目标路由的权限要求 const needAccess = to.meta?.access ?? ACCESS_ENUM.NOT_LOGIN; // 3. 验证权限 if (!checkAccess(loginUser, needAccess)) { // 无权限,跳转到登录或无权限页面 next("/noAuth"); } else { next(); } }); ``` --- ## 🔧 开发指南 ### 代码规范 #### 1. 命名规范 - **组件名**:大驼峰(PascalCase) ```typescript // ✅ Good QuestionsView.vue CodeEditor.vue // ❌ Bad questionsView.vue code-editor.vue ``` - **变量/函数名**:小驼峰(camelCase) ```typescript // ✅ Good const userName = "admin"; const getUserInfo = () => {}; // ❌ Bad const user_name = "admin"; const get_user_info = () => {}; ``` - **常量名**:全大写+下划线 ```typescript // ✅ Good const API_BASE_URL = "http://localhost:8101"; // ❌ Bad const apiBaseUrl = "http://localhost:8101"; ``` #### 2. 组件规范 使用 ` ``` #### 3. TypeScript 规范 - 所有变量和函数必须声明类型 - 优先使用 `interface` 定义对象类型 - 避免使用 `any` ```typescript // ✅ Good interface User { id: number; userName: string; userRole: string; } const getUser = (id: number): Promise => { return UserControllerService.getUserById({ id }); }; // ❌ Bad const getUser = (id: any): any => { return UserControllerService.getUserById({ id }); }; ``` ### API 调用 使用自动生成的 API 服务: ```typescript import { QuestionControllerService } from "@/generated/services/QuestionControllerService"; // 查询题目列表 const loadData = async () => { const res = await QuestionControllerService.listQuestionVoByPage({ current: 1, pageSize: 10, }); if (res.code === 0) { dataList.value = res.data.records || []; } }; ``` ### 状态管理 使用 Vuex 管理全局状态: ```typescript // 读取状态 import { useStore } from "vuex"; const store = useStore(); const loginUser = store.state.user.loginUser; // 提交 mutation store.commit("user/updateUser", newUser); // 分发 action await store.dispatch("user/getLoginUser"); ``` ### 路由跳转 ```typescript import { useRouter } from "vue-router"; const router = useRouter(); // 跳转到题目详情 router.push({ path: `/view/question/${questionId}`, }); // 带查询参数 router.push({ path: "/questions", query: { tag: "算法" }, }); ``` --- ## ⚙️ 配置说明 ### API 配置 在 `generated/core/OpenAPI.ts` 中配置后端 API 地址: ```typescript export const OpenAPI: OpenAPIConfig = { BASE: 'http://localhost:8101', // 修改为实际的后端地址 VERSION: '1.0', WITH_CREDENTIALS: true, // 携带 Cookie(Session 认证) CREDENTIALS: 'include', }; ``` ### 代理配置 在 `vite.config.ts` 中配置开发服务器代理(解决跨域): ```typescript export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8101', changeOrigin: true, }, }, }, }); ``` ### 环境变量 创建 `.env.development` 和 `.env.production` 文件: ```bash # .env.development VITE_API_BASE_URL=http://localhost:8101 # .env.production VITE_API_BASE_URL=https://api.yoursite.com ``` 使用环境变量: ```typescript const apiBaseUrl = import.meta.env.VITE_API_BASE_URL; ``` --- ## 🏗️ 构建部署 ### 本地构建 ```bash # 安装依赖 npm install # 构建生产版本 npm run build # 预览构建结果 npm run preview ``` ### 部署到 Nginx ```bash # 1. 构建项目 npm run build # 2. 将 dist/ 目录复制到 Nginx 根目录 cp -r dist/* /usr/share/nginx/html/ # 3. 配置 Nginx # 编辑 /etc/nginx/nginx.conf server { listen 80; server_name yoursite.com; location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; # SPA 路由支持 } location /api { proxy_pass http://localhost:8101; # 代理后端 API } } # 4. 重启 Nginx nginx -s reload ``` ### Docker 部署 创建 `Dockerfile`: ```dockerfile # 构建阶段 FROM node:14 AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # 运行阶段 FROM nginx:alpine COPY --from=build /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/nginx.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] ``` 构建和运行: ```bash # 构建镜像 docker build -t oj-frontend:latest . # 运行容器 docker run -d -p 80:80 oj-frontend:latest ``` --- ## 🐛 常见问题 ### 1. 依赖安装失败 ```bash # 清除缓存后重新安装 rm -rf node_modules package-lock.json npm install ``` ### 2. 编译错误 确保 Node.js 版本 >= 14.0: ```bash node -v ``` ### 3. API 请求失败 检查 `generated/core/OpenAPI.ts` 中的 BASE 地址是否正确。 ### 4. 跨域问题 确保后端已配置 CORS,或使用 Vite 代理: ```typescript // vite.config.ts export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8101', changeOrigin: true, }, }, }, }); ``` ### 5. 路由 404 部署到服务器后,刷新页面出现 404: - **原因**:SPA 应用需要服务器配置支持 - **解决**:Nginx 配置 `try_files $uri $uri/ /index.html;` --- ## 📝 更新日志 ### 2025-10-28 - v2.0.0 #### ✨ 新功能 1. **竞赛系统** - ✅ 竞赛列表和详情页面 - ✅ 竞赛创建和管理(管理员) - ✅ 报名参赛功能 - ✅ 竞赛模式下答题 - ✅ 实时排行榜 2. **每日一题** - ✅ 每日一题展示 - ✅ 打卡功能 - ✅ 打卡统计(连续天数、总天数、月度统计) 3. **题目评论** - ✅ 题目评论发布 - ✅ 嵌套式回复 - ✅ 评论展示和管理 #### 🐛 Bug 修复 1. **题目通过率显示** - 🔧 修复通过率显示为 0/0 的问题 - 🔧 优化通过率计算逻辑 2. **竞赛页面优化** - 🔧 优化竞赛状态显示 - 🔧 添加时间设置说明 - 🔧 排行榜用户信息显示优化 --- ## 🔮 未来计划 - [ ] WebSocket 实时推送(竞赛排行榜) - [ ] 代码运行结果可视化 - [ ] 题解社区功能 - [ ] 用户个人主页 - [ ] 题目收藏功能 - [ ] 评论点赞功能 - [ ] 移动端适配 - [ ] PWA 支持 --- ## 🤝 贡献指南 ### 开发流程 1. Fork 本仓库 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) 3. 提交更改 (`git commit -m 'feat: 添加新功能'`) 4. 推送到分支 (`git push origin feature/AmazingFeature`) 5. 开启 Pull Request ### Commit 规范 使用语义化提交信息: - `feat:` 新功能 - `fix:` Bug 修复 - `docs:` 文档更新 - `style:` 代码格式调整 - `refactor:` 代码重构 - `test:` 测试相关 - `chore:` 构建/工具链相关 --- ## 📄 许可证 Copyright © 2025 码农修炼手册 本项目仅供学习交流使用。 --- ## 📧 联系方式 - **项目地址**: https://gitee.com/code-farmer-training-manual - **技术支持**: 小雨村 --- **⭐ 如果这个项目对你有帮助,请给一个 Star!**