# 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!**