feat: 初始化项目脚手架
This commit is contained in:
685
doc/design/01-前端架构设计.md
Normal file
685
doc/design/01-前端架构设计.md
Normal file
@@ -0,0 +1,685 @@
|
||||
# GameGroup 前端架构设计文档
|
||||
|
||||
**项目名称**: GameGroup 前端系统
|
||||
**文档版本**: v1.0
|
||||
**更新时间**: 2026-01-28
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [1. 项目概述](#1-项目概述)
|
||||
- [2. 技术栈选型](#2-技术栈选型)
|
||||
- [3. 项目架构](#3-项目架构)
|
||||
- [4. 目录结构](#4-目录结构)
|
||||
- [5. 核心设计原则](#5-核心设计原则)
|
||||
- [6. 状态管理](#6-状态管理)
|
||||
- [7. 路由设计](#7-路由设计)
|
||||
- [8. API请求层](#8-api请求层)
|
||||
- [9. 性能优化](#9-性能优化)
|
||||
|
||||
---
|
||||
|
||||
## 1. 项目概述
|
||||
|
||||
GameGroup 是一个游戏社群管理平台,为玩家提供小组管理、游戏预约、积分系统、竞猜等功能。前端项目采用现代化的技术栈,注重用户体验和性能优化。
|
||||
|
||||
### 核心功能模块
|
||||
|
||||
- **认证模块**: 用户注册、登录、令牌刷新
|
||||
- **用户中心**: 个人资料管理、设置
|
||||
- **小组管理**: 创建/加入小组、成员管理、权限控制
|
||||
- **游戏库**: 游戏浏览、搜索、筛选
|
||||
- **预约系统**: 创建预约、加入/退出预约、时间管理
|
||||
- **账目管理**: 账目记录、统计分析
|
||||
- **排班助手**: 空闲时间提交、共同时间查找
|
||||
- **荣誉墙**: 荣誉展示、时间轴
|
||||
- **资产管理**: 资产借用/归还、流转记录
|
||||
- **积分系统**: 积分查询、排行榜
|
||||
- **竞猜系统**: 创建竞猜、参与竞猜、结算
|
||||
|
||||
---
|
||||
|
||||
## 2. 技术栈选型
|
||||
|
||||
### 核心框架
|
||||
|
||||
```json
|
||||
{
|
||||
"framework": "Vue 3",
|
||||
"buildTool": "Vite",
|
||||
"language": "TypeScript",
|
||||
"router": "Vue Router 4",
|
||||
"stateManagement": "Pinia",
|
||||
"uiFramework": "Element Plus / Ant Design Vue",
|
||||
"cssFramework": "Tailwind CSS / UnoCSS",
|
||||
"http": "Axios"
|
||||
}
|
||||
```
|
||||
|
||||
### 选型理由
|
||||
|
||||
#### Vue 3 + TypeScript
|
||||
- **组合式 API**: 更好的逻辑复用和代码组织
|
||||
- **类型安全**: TypeScript提供完整的类型推导和检查
|
||||
- **性能提升**: 更小的包体积、更快的渲染
|
||||
- **生态成熟**: 丰富的插件和工具支持
|
||||
|
||||
#### Vite
|
||||
- **极速开发**: 基于ESM的即时热更新
|
||||
- **快速构建**: Rollup打包,生产环境优化
|
||||
- **现代化**: 原生支持ES模块、TypeScript、JSX
|
||||
|
||||
#### Pinia
|
||||
- **Vue 3官方推荐**: 替代Vuex的下一代状态管理
|
||||
- **类型友好**: 完整的TypeScript支持
|
||||
- **轻量简洁**: API设计简洁,学习成本低
|
||||
- **Devtools**: 优秀的开发者工具集成
|
||||
|
||||
#### Vue Router 4
|
||||
- **官方路由**: Vue生态标准路由方案
|
||||
- **动态路由**: 支持路由级代码分割
|
||||
- **导航守卫**: 完善的权限控制
|
||||
|
||||
---
|
||||
|
||||
## 3. 项目架构
|
||||
|
||||
### 3.1 分层架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Presentation Layer │
|
||||
│ (Components / Views / Layouts) │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Business Logic Layer │
|
||||
│ (Composables / Stores / Hooks) │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ Data Access Layer │
|
||||
│ (API Services / Local Storage) │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 模块化设计
|
||||
|
||||
每个功能模块包含:
|
||||
- **Views**: 页面级组件
|
||||
- **Components**: 模块内组件
|
||||
- **Composables**: 业务逻辑复用
|
||||
- **Stores**: 状态管理
|
||||
- **API**: 接口请求
|
||||
- **Types**: TypeScript类型定义
|
||||
- **Utils**: 工具函数
|
||||
|
||||
---
|
||||
|
||||
## 4. 目录结构
|
||||
|
||||
```
|
||||
GameGroupFE/
|
||||
├── public/ # 静态资源
|
||||
│ ├── favicon.ico
|
||||
│ └── ...
|
||||
├── src/
|
||||
│ ├── assets/ # 资源文件
|
||||
│ │ ├── images/ # 图片
|
||||
│ │ ├── icons/ # 图标
|
||||
│ │ └── styles/ # 全局样式
|
||||
│ │ ├── variables.scss # 样式变量
|
||||
│ │ ├── mixins.scss # 样式混入
|
||||
│ │ └── global.scss # 全局样式
|
||||
│ ├── components/ # 公共组件
|
||||
│ │ ├── common/ # 通用组件
|
||||
│ │ │ ├── Button/
|
||||
│ │ │ ├── Input/
|
||||
│ │ │ ├── Modal/
|
||||
│ │ │ └── Loading/
|
||||
│ │ ├── business/ # 业务组件
|
||||
│ │ │ ├── UserCard/
|
||||
│ │ │ ├── GroupCard/
|
||||
│ │ │ └── GameCard/
|
||||
│ │ └── layout/ # 布局组件
|
||||
│ │ ├── Header/
|
||||
│ │ ├── Sidebar/
|
||||
│ │ └── Footer/
|
||||
│ ├── composables/ # 组合式函数
|
||||
│ │ ├── useAuth.ts # 认证相关
|
||||
│ │ ├── useRequest.ts # 请求封装
|
||||
│ │ ├── usePagination.ts # 分页逻辑
|
||||
│ │ └── useWebSocket.ts # WebSocket
|
||||
│ ├── stores/ # Pinia状态管理
|
||||
│ │ ├── auth.ts # 认证状态
|
||||
│ │ ├── user.ts # 用户状态
|
||||
│ │ ├── group.ts # 小组状态
|
||||
│ │ └── app.ts # 应用状态
|
||||
│ ├── api/ # API接口
|
||||
│ │ ├── index.ts # Axios配置
|
||||
│ │ ├── auth.ts # 认证接口
|
||||
│ │ ├── user.ts # 用户接口
|
||||
│ │ ├── group.ts # 小组接口
|
||||
│ │ └── ...
|
||||
│ ├── router/ # 路由配置
|
||||
│ │ ├── index.ts # 路由入口
|
||||
│ │ ├── routes/ # 路由模块
|
||||
│ │ └── guards.ts # 路由守卫
|
||||
│ ├── views/ # 页面视图
|
||||
│ │ ├── auth/ # 认证相关页面
|
||||
│ │ │ ├── Login.vue
|
||||
│ │ │ └── Register.vue
|
||||
│ │ ├── home/ # 首页
|
||||
│ │ │ └── index.vue
|
||||
│ │ ├── user/ # 用户中心
|
||||
│ │ │ ├── Profile.vue
|
||||
│ │ │ └── Settings.vue
|
||||
│ │ ├── group/ # 小组管理
|
||||
│ │ │ ├── List.vue
|
||||
│ │ │ ├── Detail.vue
|
||||
│ │ │ └── Create.vue
|
||||
│ │ ├── game/ # 游戏库
|
||||
│ │ ├── appointment/ # 预约管理
|
||||
│ │ └── ...
|
||||
│ ├── types/ # TypeScript类型定义
|
||||
│ │ ├── api.ts # API响应类型
|
||||
│ │ ├── user.ts # 用户类型
|
||||
│ │ ├── group.ts # 小组类型
|
||||
│ │ └── ...
|
||||
│ ├── utils/ # 工具函数
|
||||
│ │ ├── request.ts # 请求工具
|
||||
│ │ ├── storage.ts # 存储工具
|
||||
│ │ ├── format.ts # 格式化工具
|
||||
│ │ └── validate.ts # 验证工具
|
||||
│ ├── constants/ # 常量定义
|
||||
│ │ ├── enums.ts # 枚举
|
||||
│ │ └── config.ts # 配置
|
||||
│ ├── directives/ # 自定义指令
|
||||
│ │ └── permission.ts
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.ts # 应用入口
|
||||
├── tests/ # 测试文件
|
||||
│ ├── unit/ # 单元测试
|
||||
│ └── e2e/ # E2E测试
|
||||
├── .env.development # 开发环境变量
|
||||
├── .env.production # 生产环境变量
|
||||
├── .eslintrc.cjs # ESLint配置
|
||||
├── .prettierrc # Prettier配置
|
||||
├── tsconfig.json # TypeScript配置
|
||||
├── vite.config.ts # Vite配置
|
||||
├── tailwind.config.js # Tailwind配置
|
||||
└── package.json # 项目配置
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 核心设计原则
|
||||
|
||||
### 5.1 组件设计原则
|
||||
|
||||
#### 单一职责原则
|
||||
每个组件只负责一个功能,保持组件的独立性和可复用性。
|
||||
|
||||
#### 组合优于继承
|
||||
使用组合式API和props/events进行组件通信,避免复杂的继承关系。
|
||||
|
||||
#### 容器组件与展示组件分离
|
||||
- **容器组件**: 处理业务逻辑和状态管理
|
||||
- **展示组件**: 纯UI渲染,通过props接收数据
|
||||
|
||||
### 5.2 代码规范
|
||||
|
||||
#### 命名规范
|
||||
- **组件**: PascalCase (UserCard.vue)
|
||||
- **文件**: kebab-case (user-profile.ts)
|
||||
- **变量/函数**: camelCase (getUserInfo)
|
||||
- **常量**: UPPER_SNAKE_CASE (API_BASE_URL)
|
||||
- **类型/接口**: PascalCase (UserProfile)
|
||||
|
||||
#### 文件组织
|
||||
- 一个文件只导出一个主要内容
|
||||
- 相关文件放在同一目录
|
||||
- 使用index.ts统一导出
|
||||
|
||||
#### 注释规范
|
||||
```typescript
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param userId - 用户ID
|
||||
* @returns 用户信息对象
|
||||
*/
|
||||
async function getUserInfo(userId: string): Promise<User> {
|
||||
// 实现
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 状态管理
|
||||
|
||||
### 6.1 Pinia Store设计
|
||||
|
||||
#### 模块化Store
|
||||
每个业务模块创建独立的Store:
|
||||
|
||||
```typescript
|
||||
// stores/user.ts
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// State
|
||||
const userInfo = ref<UserInfo | null>(null)
|
||||
const isLoading = ref(false)
|
||||
|
||||
// Getters
|
||||
const isLoggedIn = computed(() => !!userInfo.value)
|
||||
|
||||
// Actions
|
||||
async function fetchUserInfo() {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const data = await userApi.getProfile()
|
||||
userInfo.value = data
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
userInfo,
|
||||
isLoading,
|
||||
isLoggedIn,
|
||||
fetchUserInfo
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 全局Store
|
||||
应用级别的状态:
|
||||
|
||||
```typescript
|
||||
// stores/app.ts
|
||||
export const useAppStore = defineStore('app', () => {
|
||||
const theme = ref<Theme>('light')
|
||||
const language = ref<Language>('zh-CN')
|
||||
const sidebarCollapsed = ref(false)
|
||||
|
||||
function toggleTheme() {
|
||||
theme.value = theme.value === 'light' ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
return {
|
||||
theme,
|
||||
language,
|
||||
sidebarCollapsed,
|
||||
toggleTheme
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 6.2 持久化策略
|
||||
|
||||
使用`pinia-plugin-persistedstate`实现状态持久化:
|
||||
|
||||
```typescript
|
||||
// pinia.ts
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
export default pinia
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 路由设计
|
||||
|
||||
### 7.1 路由结构
|
||||
|
||||
```typescript
|
||||
// router/index.ts
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
component: () => import('@/views/auth/Login.vue'),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('@/components/layout/MainLayout.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'Home',
|
||||
component: () => import('@/views/home/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'groups',
|
||||
name: 'GroupList',
|
||||
component: () => import('@/views/group/List.vue')
|
||||
},
|
||||
{
|
||||
path: 'groups/:id',
|
||||
name: 'GroupDetail',
|
||||
component: () => import('@/views/group/Detail.vue')
|
||||
},
|
||||
// 更多路由...
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 7.2 路由守卫
|
||||
|
||||
```typescript
|
||||
// router/guards.ts
|
||||
router.beforeEach((to, from, next) => {
|
||||
const authStore = useAuthStore()
|
||||
const requiresAuth = to.meta.requiresAuth !== false
|
||||
|
||||
if (requiresAuth && !authStore.isLoggedIn) {
|
||||
// 未登录,跳转到登录页
|
||||
next({
|
||||
name: 'Login',
|
||||
query: { redirect: to.fullPath }
|
||||
})
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. API请求层
|
||||
|
||||
### 8.1 Axios封装
|
||||
|
||||
```typescript
|
||||
// api/index.ts
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL,
|
||||
timeout: 15000
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
const authStore = useAuthStore()
|
||||
if (authStore.token) {
|
||||
config.headers.Authorization = `Bearer ${authStore.token}`
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
const { code, message, data } = response.data
|
||||
|
||||
if (code === 0) {
|
||||
return data
|
||||
} else {
|
||||
ElMessage.error(message || '请求失败')
|
||||
return Promise.reject(new Error(message))
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (error.response?.status === 401) {
|
||||
// Token过期,刷新Token或跳转登录
|
||||
const authStore = useAuthStore()
|
||||
authStore.logout()
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
```
|
||||
|
||||
### 8.2 API模块化
|
||||
|
||||
```typescript
|
||||
// api/user.ts
|
||||
import request from './index'
|
||||
|
||||
export const userApi = {
|
||||
// 获取用户信息
|
||||
getProfile() {
|
||||
return request.get<UserInfo>('/users/me')
|
||||
},
|
||||
|
||||
// 更新用户信息
|
||||
updateProfile(data: UpdateProfileDto) {
|
||||
return request.put<UserInfo>('/users/me', data)
|
||||
},
|
||||
|
||||
// 修改密码
|
||||
changePassword(data: ChangePasswordDto) {
|
||||
return request.put('/users/me/password', data)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 性能优化
|
||||
|
||||
### 9.1 代码分割
|
||||
|
||||
#### 路由级代码分割
|
||||
使用动态import实现路由懒加载:
|
||||
|
||||
```typescript
|
||||
{
|
||||
path: '/groups',
|
||||
component: () => import('@/views/group/List.vue')
|
||||
}
|
||||
```
|
||||
|
||||
#### 组件级代码分割
|
||||
```vue
|
||||
<script setup>
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const HeavyComponent = defineAsyncComponent(() =>
|
||||
import('./HeavyComponent.vue')
|
||||
)
|
||||
</script>
|
||||
```
|
||||
|
||||
### 9.2 资源优化
|
||||
|
||||
#### 图片优化
|
||||
- 使用WebP格式
|
||||
- 实现懒加载
|
||||
- 响应式图片
|
||||
|
||||
#### 依赖优化
|
||||
- Tree-shaking
|
||||
- 按需引入组件库
|
||||
- 使用轻量级替代方案
|
||||
|
||||
### 9.3 渲染优化
|
||||
|
||||
#### 虚拟滚动
|
||||
长列表使用虚拟滚动:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<VirtualList
|
||||
:items="largeList"
|
||||
:item-height="50"
|
||||
:visible-height="600"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### 防抖与节流
|
||||
```typescript
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
const search = useDebounceFn((keyword: string) => {
|
||||
// 执行搜索
|
||||
}, 300)
|
||||
```
|
||||
|
||||
### 9.4 缓存策略
|
||||
|
||||
#### 接口缓存
|
||||
```typescript
|
||||
// api/cache.ts
|
||||
const cache = new Map()
|
||||
|
||||
export async function cachedFetch<T>(
|
||||
key: string,
|
||||
fetcher: () => Promise<T>,
|
||||
ttl = 60000
|
||||
): Promise<T> {
|
||||
const cached = cache.get(key)
|
||||
if (cached && Date.now() - cached.time < ttl) {
|
||||
return cached.data
|
||||
}
|
||||
|
||||
const data = await fetcher()
|
||||
cache.set(key, { data, time: Date.now() })
|
||||
return data
|
||||
}
|
||||
```
|
||||
|
||||
#### 组件缓存
|
||||
```vue
|
||||
<KeepAlive :include="['GroupList', 'GameList']">
|
||||
<RouterView />
|
||||
</KeepAlive>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 开发工具链
|
||||
|
||||
### 10.1 代码质量
|
||||
|
||||
- **ESLint**: 代码规范检查
|
||||
- **Prettier**: 代码格式化
|
||||
- **Stylelint**: 样式规范检查
|
||||
- **TypeScript**: 类型检查
|
||||
|
||||
### 10.2 Git规范
|
||||
|
||||
- **Husky**: Git hooks
|
||||
- **lint-staged**: 暂存文件检查
|
||||
- **Commitlint**: 提交信息规范
|
||||
|
||||
### 10.3 CI/CD
|
||||
|
||||
- **GitHub Actions**: 自动化部署
|
||||
- **Vercel/Netlify**: 前端托管
|
||||
|
||||
---
|
||||
|
||||
## 11. 测试策略
|
||||
|
||||
### 11.1 单元测试
|
||||
|
||||
使用Vitest进行单元测试:
|
||||
|
||||
```typescript
|
||||
// tests/utils/format.test.ts
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { formatDate } from '@/utils/format'
|
||||
|
||||
describe('formatDate', () => {
|
||||
it('should format date correctly', () => {
|
||||
const date = new Date('2026-01-28')
|
||||
expect(formatDate(date)).toBe('2026-01-28')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### 11.2 组件测试
|
||||
|
||||
使用Vue Test Utils:
|
||||
|
||||
```typescript
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Button from '@/components/common/Button/Button.vue'
|
||||
|
||||
describe('Button', () => {
|
||||
it('renders text', () => {
|
||||
const wrapper = mount(Button, {
|
||||
slots: { default: 'Click me' }
|
||||
})
|
||||
expect(wrapper.text()).toBe('Click me')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 部署方案
|
||||
|
||||
### 12.1 构建配置
|
||||
|
||||
```typescript
|
||||
// vite.config.ts
|
||||
export default defineConfig({
|
||||
build: {
|
||||
target: 'es2015',
|
||||
outDir: 'dist',
|
||||
assetsDir: 'assets',
|
||||
sourcemap: false,
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true
|
||||
}
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vendor': ['vue', 'vue-router', 'pinia'],
|
||||
'ui': ['element-plus']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 12.2 环境变量
|
||||
|
||||
```bash
|
||||
# .env.development
|
||||
VITE_API_BASE_URL=http://localhost:3000/api
|
||||
|
||||
# .env.production
|
||||
VITE_API_BASE_URL=https://api.gamegroup.com/api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. 总结
|
||||
|
||||
本架构设计遵循以下原则:
|
||||
- ✅ 模块化: 清晰的模块划分,职责明确
|
||||
- ✅ 可维护性: 规范的代码组织,易于维护
|
||||
- ✅ 可扩展性: 灵活的架构设计,便于扩展
|
||||
- ✅ 性能优化: 多层次的优化策略
|
||||
- ✅ 开发体验: 完善的工具链支持
|
||||
|
||||
---
|
||||
|
||||
**文档维护**: 本文档应随项目演进持续更新
|
||||
**最后更新**: 2026-01-28
|
||||
Reference in New Issue
Block a user