Files
gym-manage/docs/design/前端开发规范.md
T
2026-03-05 13:48:13 +08:00

1018 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 健身房管理系统前端开发规范文档
> 文档编号: GYM-FE-DEV-001
> 版本: v1.0
> 日期: 2026-03-04
> 作者: 张翔
> 状态: 初稿
---
## 文档修订历史
| 版本 | 日期 | 作者 | 修订内容 |
| ---- | ---------- | ---- | -------- |
| v1.0 | 2026-03-04 | 张翔 | 创建前端开发规范 |
---
## 参考文档
- 《健身房管理系统前端技术架构详细设计》 GYM-FE-ARCH-001
- Vue 3 风格指南
- TypeScript 编码规范
- Airbnb JavaScript Style Guide
---
## 一、编码规范
### 1.1 代码风格
#### 1.1.1 使用ESLint
```json
// .eslintrc.json
{
"extends": [
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"vue/multi-word-component-names": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"no-console": ["warn", { "allow": ["warn", "error"] }]
}
}
```
#### 1.1.2 使用Prettier
```json
// .prettierrc
{
"semi": false,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "es5",
"arrowParens": "avoid",
"endOfLine": "lf"
}
```
### 1.2 命名规范
#### 1.2.1 文件命名
| 类型 | 命名规范 | 示例 |
|------|---------|------|
| **组件文件** | PascalCase | MemberList.vue, CourseCard.vue |
| **工具文件** | camelCase | formatDate.ts, validator.ts |
| **类型文件** | camelCase | api.ts, models.ts |
| **常量文件** | UPPER_SNAKE_CASE | API_BASE_URL.ts |
| **样式文件** | kebab-case | member-list.scss, button.css |
#### 1.2.2 变量命名
```typescript
// 布尔值:使用is/has/can前缀
const isActive = true
const hasPermission = false
const canEdit = true
// 常量:使用UPPER_SNAKE_CASE
const API_BASE_URL = 'https://api.example.com'
const MAX_RETRY_COUNT = 3
// 函数:使用camelCase,动词开头
function getMemberList() {}
function validatePhoneNumber() {}
function handleSearch() {}
// 类:使用PascalCase
class UserService {}
class MemberValidator {}
// 接口:使用PascalCaseI前缀可选
interface Member {}
interface IMember {}
// 类型别名:使用PascalCase
type MemberStatus = 'active' | 'inactive'
// 枚举:使用PascalCase
enum MemberLevel {
BRONZE = 1,
SILVER = 2,
GOLD = 3
}
```
#### 1.2.3 组件命名
```vue
<!-- 组件名称使用PascalCase -->
<template>
<MemberList />
<CourseCard />
</template>
<script setup lang="ts">
// 组件文件名与组件名一致
const componentName = 'MemberList'
</script>
```
### 1.3 代码格式
#### 1.3.1 缩进与空格
```typescript
// 使用2个空格缩进
function example() {
if (condition) {
doSomething()
}
}
// 对象和数组使用空格
const obj = { a: 1, b: 2 }
const arr = [1, 2, 3]
// 运算符前后使用空格
const sum = a + b
const result = a > b ? a : b
```
#### 1.3.2 引号使用
```typescript
// 优先使用单引号
const name = 'John'
const message = 'Hello, world!'
// 字符串中包含单引号时使用双引号
const quote = "It's a beautiful day"
// 模板字符串使用反引号
const greeting = `Hello, ${name}!`
```
#### 1.3.3 分号使用
```typescript
// 不使用分号
const a = 1
const b = 2
function example() {
return a + b
}
```
---
## 二、Vue组件规范
### 2.1 组件结构
```vue
<template>
<!-- 模板内容 -->
</template>
<script setup lang="ts">
// 导入
import { ref, computed, onMounted } from 'vue'
import { useAuthStore } from '@/stores/auth'
// Props定义
interface Props {
title: string
count?: number
}
const props = withDefaults(defineProps<Props>(), {
count: 0
})
// Emits定义
const emit = defineEmits<{
click: [event: MouseEvent]
change: [value: string]
}>()
// 响应式数据
const count = ref(0)
const message = ref('Hello')
// 计算属性
const doubled = computed(() => count.value * 2)
// 方法
const handleClick = (event: MouseEvent) => {
emit('click', event)
}
// 生命周期
onMounted(() => {
console.log('Component mounted')
})
</script>
<style scoped lang="scss">
/* 样式内容 */
</style>
```
### 2.2 Props定义
```typescript
// 使用TypeScript接口定义Props
interface Props {
title: string
count?: number
items: string[]
config: {
enabled: boolean
timeout: number
}
}
// 使用withDefaults设置默认值
const props = withDefaults(defineProps<Props>(), {
count: 0,
items: () => [],
config: () => ({ enabled: true, timeout: 5000 })
})
```
### 2.3 Emits定义
```typescript
// 使用TypeScript定义Emits
const emit = defineEmits<{
click: [event: MouseEvent]
change: [value: string]
submit: [data: FormData]
cancel: []
}>()
// 触发事件
emit('click', event)
emit('change', newValue)
emit('submit', formData)
emit('cancel')
```
### 2.4 响应式数据
```typescript
// 使用ref定义基本类型
const count = ref(0)
const message = ref('Hello')
// 使用reactive定义对象
const user = reactive({
name: 'John',
age: 30
})
// 使用computed定义计算属性
const fullName = computed(() => `${user.name} is ${user.age} years old`)
// 使用readonly定义只读数据
const readonlyData = readonly({ id: 1, name: 'John' })
// 使用shallowRef和shallowReactive优化性能
const largeList = shallowRef<any[]>([])
const largeObject = shallowReactive({})
```
### 2.5 组件通信
#### 2.5.1 Props传递
```vue
<!-- 父组件 -->
<template>
<child-component
:title="pageTitle"
:count="itemCount"
@update="handleUpdate"
/>
</template>
<script setup lang="ts">
import ChildComponent from './ChildComponent.vue'
const pageTitle = ref('Dashboard')
const itemCount = ref(10)
const handleUpdate = (value: any) => {
console.log('Update:', value)
}
</script>
```
#### 2.5.2 事件传递
```vue
<!-- 子组件 -->
<template>
<button @click="handleClick">Click me</button>
</template>
<script setup lang="ts">
const emit = defineEmits<{
click: [event: MouseEvent]
}>()
const handleClick = (event: MouseEvent) => {
emit('click', event)
}
</script>
```
#### 2.5.3 Provide/Inject
```typescript
// 父组件
import { provide } from 'vue'
const theme = ref('light')
provide('theme', theme)
// 子组件
import { inject } from 'vue'
const theme = inject<Ref<string>>('theme')
```
---
## 三、TypeScript规范
### 3.1 类型定义
#### 3.1.1 基础类型
```typescript
// 使用明确的类型
const count: number = 10
const name: string = 'John'
const isActive: boolean = true
const items: string[] = ['a', 'b', 'c']
const user: { name: string; age: number } = { name: 'John', age: 30 }
```
#### 3.1.2 接口定义
```typescript
// 定义接口
interface Member {
id: number
name: string
phone: string
level: MemberLevel
status: MemberStatus
}
// 可选属性
interface User {
id: number
name: string
email?: string
}
// 只读属性
interface Config {
readonly id: number
name: string
}
// 索引签名
interface StringDictionary {
[key: string]: string
}
```
#### 3.1.3 类型别名
```typescript
// 定义类型别名
type MemberStatus = 'active' | 'inactive' | 'frozen'
type MemberLevel = 1 | 2 | 3 | 4 | 5
// 联合类型
type ID = number | string
// 交叉类型
type Name = { firstName: string; lastName: string }
type Age = { age: number }
type Person = Name & Age
```
#### 3.1.4 泛型
```typescript
// 泛型函数
function identity<T>(arg: T): T {
return arg
}
// 泛型接口
interface Response<T> {
code: number
data: T
message: string
}
// 泛型类
class Storage<T> {
private items: T[] = []
add(item: T): void {
this.items.push(item)
}
get(index: number): T | undefined {
return this.items[index]
}
}
```
### 3.2 类型断言
```typescript
// 使用as关键字进行类型断言
const element = document.getElementById('app') as HTMLElement
// 使用尖括号语法(JSX中不可用)
const element2 = <HTMLElement>document.getElementById('app')
// 非空断言
const value = input.value!
```
### 3.3 类型守卫
```typescript
// typeof类型守卫
function processValue(value: string | number) {
if (typeof value === 'string') {
console.log(value.toUpperCase())
} else {
console.log(value.toFixed(2))
}
}
// instanceof类型守卫
class Dog {
bark() {}
}
class Cat {
meow() {}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark()
} else {
animal.meow()
}
}
// 自定义类型守卫
interface Member {
id: number
name: string
type: 'individual' | 'corporate'
}
function isCorporateMember(member: Member): member is Member & { type: 'corporate' } {
return member.type === 'corporate'
}
```
---
## 四、注释规范
### 4.1 文件注释
```typescript
/**
* 会员服务
*
* 提供会员相关的业务逻辑处理
*
* @example
* const memberService = new MemberService()
* const members = await memberService.getList()
*/
export class MemberService {
// ...
}
```
### 4.2 函数注释
```typescript
/**
* 获取会员列表
*
* @param params - 查询参数
* @param params.page - 页码
* @param params.pageSize - 每页数量
* @param params.keyword - 搜索关键词
* @returns 会员列表数据
* @throws {Error} 当API请求失败时抛出错误
*
* @example
* const result = await getMemberList({ page: 1, pageSize: 10 })
*/
export async function getMemberList(params: {
page: number
pageSize: number
keyword?: string
}): Promise<MemberListResponse> {
// ...
}
```
### 4.3 类注释
```typescript
/**
* 会员验证器
*
* 提供会员数据验证功能
*/
export class MemberValidator {
/**
* 验证手机号
*
* @param phone - 手机号
* @returns 验证结果
*/
validatePhone(phone: string): ValidationResult {
// ...
}
}
```
### 4.4 行内注释
```typescript
// 计算会员等级
const level = calculateLevel(exp)
// TODO: 需要优化这个算法的性能
const result = complexCalculation(data)
// FIXME: 这里有个bug,需要修复
const value = buggyFunction()
// HACK: 临时解决方案,后续需要重构
const temp = workaround(data)
```
---
## 五、文件组织规范
### 5.1 目录结构
```
src/
├── api/ # API接口
│ ├── modules/ # API模块
│ │ ├── auth.ts
│ │ ├── member.ts
│ │ └── booking.ts
│ └── request.ts # Axios封装
├── assets/ # 静态资源
│ ├── images/ # 图片
│ ├── icons/ # 图标
│ └── styles/ # 样式
├── components/ # 组件
│ ├── base/ # 基础组件
│ ├── business/ # 业务组件
│ └── layout/ # 布局组件
├── composables/ # Composables
├── config/ # 配置
├── directives/ # 自定义指令
├── hooks/ # Hooks
├── layouts/ # 布局
├── router/ # 路由
├── stores/ # 状态管理
├── types/ # 类型定义
├── utils/ # 工具函数
├── views/ # 页面
├── App.vue
└── main.ts
```
### 5.2 文件导入顺序
```typescript
// 1. Vue相关导入
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
// 2. 第三方库导入
import axios from 'axios'
import dayjs from 'dayjs'
// 3. 内部模块导入
import { useAuthStore } from '@/stores/auth'
import { formatDate } from '@/utils/formatter'
// 4. 类型导入
import type { Member, MemberListParams } from '@/types/models'
// 5. 样式导入
import './styles/index.scss'
```
### 5.3 导出规范
```typescript
// 命名导出
export function formatDate(date: Date): string {}
export function formatTime(time: Date): string {}
// 默认导出(仅用于组件)
export default defineComponent({
// ...
})
// 类型导出
export type { Member, MemberStatus }
export interface { MemberListParams }
```
---
## 六、Git提交规范
### 6.1 提交信息格式
```
<type>(<scope>): <subject>
<body>
<footer>
```
### 6.2 Type类型
| Type | 描述 | 示例 |
|------|------|------|
| **feat** | 新功能 | feat(member): add member list page |
| **fix** | 修复bug | fix(booking): resolve booking conflict |
| **docs** | 文档更新 | docs(readme): update installation guide |
| **style** | 代码格式调整 | style(member): format member component |
| **refactor** | 重构 | refactor(auth): simplify login logic |
| **perf** | 性能优化 | perf(list): optimize virtual scroll |
| **test** | 测试相关 | test(member): add member validator tests |
| **chore** | 构建过程或辅助工具变动 | chore(deps): update dependencies |
| **ci** | CI配置文件更新 | ci(github): add workflow config |
### 6.3 Scope范围
| Scope | 描述 |
|-------|------|
| **auth** | 认证相关 |
| **member** | 会员相关 |
| **booking** | 预约相关 |
| **checkin** | 签到相关 |
| **course** | 课程相关 |
| **finance** | 财务相关 |
| **data** | 数据相关 |
| **system** | 系统相关 |
| **ui** | UI组件 |
| **utils** | 工具函数 |
### 6.4 提交示例
```bash
# 新功能
git commit -m "feat(member): add member search functionality"
# 修复bug
git commit -m "fix(booking): resolve booking time conflict issue"
# 文档更新
git commit -m "docs(readme): update project setup instructions"
# 性能优化
git commit -m "perf(list): optimize virtual scroll performance"
# 重构
git commit -m "refactor(auth): simplify authentication flow"
```
---
## 七、代码审查规范
### 7.1 审查清单
#### 7.1.1 功能性
- [ ] 代码实现了需求文档中的功能
- [ ] 代码逻辑正确,没有明显的bug
- [ ] 边界情况得到处理
- [ ] 错误处理完善
#### 7.1.2 代码质量
- [ ] 代码符合项目编码规范
- [ ] 代码结构清晰,易于理解
- [ ] 代码有适当的注释
- [ ] 变量和函数命名准确
#### 7.1.3 性能
- [ ] 没有明显的性能问题
- [ ] 使用了适当的优化技术
- [ ] 没有不必要的重复计算
- [ ] 大数据量处理使用了虚拟滚动
#### 7.1.4 安全性
- [ ] 输入验证完善
- [ ] 敏感数据加密存储
- [ ] 没有XSS、CSRF等安全漏洞
- [ ] 权限控制正确
#### 7.1.5 测试
- [ ] 单元测试覆盖率达标
- [ ] 测试用例充分
- [ ] 测试通过
- [ ] 没有测试警告
### 7.2 审查流程
1. **提交Pull Request**
- 确保代码通过所有检查
- 编写清晰的PR描述
- 关联相关的Issue
2. **代码审查**
- 至少一名审查者批准
- 解决所有审查意见
- 确保没有冲突
3. **合并代码**
- 确保CI/CD通过
- 删除功能分支
- 更新相关文档
---
## 八、最佳实践
### 8.1 组件设计
#### 8.1.1 单一职责
```vue
<!-- Bad: 组件职责过多 -->
<template>
<div>
<input v-model="form.name" />
<input v-model="form.phone" />
<button @click="submit">提交</button>
<div v-if="showChart">
<chart :data="chartData" />
</div>
</div>
</template>
<!-- Good: 组件职责单一 -->
<template>
<member-form :model="form" @submit="handleSubmit" />
<data-chart v-if="showChart" :data="chartData" />
</template>
```
#### 8.1.2 Props验证
```typescript
// 使用TypeScript接口定义Props
interface Props {
title: string
count?: number
items: string[]
}
const props = withDefaults(defineProps<Props>(), {
count: 0,
items: () => []
})
```
### 8.2 状态管理
#### 8.2.1 合理使用状态
```typescript
// 全局状态:用户信息、权限等
const authStore = useAuthStore()
// 模块状态:当前页面数据
const pageData = ref({})
// 组件状态:弹窗显示、加载状态
const showModal = ref(false)
const loading = ref(false)
```
#### 8.2.2 避免状态冗余
```typescript
// Bad: 冗余状态
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// Good: 单一数据源
const user = reactive({
firstName: 'John',
lastName: 'Doe'
})
const fullName = computed(() => `${user.firstName} ${user.lastName}`)
```
### 8.3 错误处理
#### 8.3.1 统一错误处理
```typescript
// utils/errorHandler.ts
export function handleError(error: unknown): void {
if (error instanceof Error) {
console.error('Error:', error.message)
// 显示错误提示
showErrorToast(error.message)
} else {
console.error('Unknown error:', error)
showErrorToast('发生未知错误')
}
}
// 使用
try {
await api.getMemberList()
} catch (error) {
handleError(error)
}
```
#### 8.3.2 错误边界
```vue
<template>
<error-boundary @error="handleError">
<router-view />
</error-boundary>
</template>
<script setup lang="ts">
const handleError = (error: Error) => {
console.error('Error caught by boundary:', error)
}
</script>
```
### 8.4 性能优化
#### 8.4.1 避免不必要的响应式
```typescript
// Bad: 所有数据都是响应式
const config = reactive({
apiBaseUrl: 'https://api.example.com',
timeout: 5000,
maxRetries: 3
})
// Good: 常量不需要响应式
const API_BASE_URL = 'https://api.example.com'
const TIMEOUT = 5000
const MAX_RETRIES = 3
```
#### 8.4.2 使用计算属性缓存
```typescript
// Good: 使用computed缓存结果
const filteredMembers = computed(() => {
return memberList.value.filter(member =>
member.name.includes(searchKeyword.value)
)
})
```
---
## 九、开发流程
### 9.1 分支管理
```
main (主分支)
├── develop (开发分支)
│ ├── feature/member-list (功能分支)
│ ├── feature/booking-system (功能分支)
│ └── fix/booking-bug (修复分支)
└── release/v1.0.0 (发布分支)
```
### 9.2 开发流程
1. **创建功能分支**
```bash
git checkout develop
git pull origin develop
git checkout -b feature/member-list
```
2. **开发功能**
- 按照编码规范编写代码
- 编写单元测试
- 本地测试通过
3. **提交代码**
```bash
git add .
git commit -m "feat(member): add member list page"
git push origin feature/member-list
```
4. **创建Pull Request**
- 编写清晰的PR描述
- 关联相关的Issue
- 请求代码审查
5. **代码审查**
- 至少一名审查者批准
- 解决所有审查意见
6. **合并代码**
- 合并到develop分支
- 删除功能分支
### 9.3 发布流程
1. **创建发布分支**
```bash
git checkout develop
git pull origin develop
git checkout -b release/v1.0.0
```
2. **发布准备**
- 更新版本号
- 更新CHANGELOG
- 运行完整测试
3. **合并到main**
```bash
git checkout main
git merge release/v1.0.0
git tag v1.0.0
git push origin main --tags
```
4. **合并回develop**
```bash
git checkout develop
git merge release/v1.0.0
git push origin develop
```
---
## 十、总结
本文档详细描述了健身房管理系统前端的开发规范,包括:
1. **编码规范**:代码风格、命名规范、代码格式
2. **Vue组件规范**:组件结构、Props定义、Emits定义、响应式数据、组件通信
3. **TypeScript规范**:类型定义、类型断言、类型守卫
4. **注释规范**:文件注释、函数注释、类注释、行内注释
5. **文件组织规范**:目录结构、文件导入顺序、导出规范
6. **Git提交规范**:提交信息格式、Type类型、Scope范围、提交示例
7. **代码审查规范**:审查清单、审查流程
8. **最佳实践**:组件设计、状态管理、错误处理、性能优化
9. **开发流程**:分支管理、开发流程、发布流程
通过遵循本文档的开发规范,可以确保代码质量、提高开发效率、降低维护成本。