1018 lines
19 KiB
Markdown
1018 lines
19 KiB
Markdown
# 健身房管理系统前端开发规范文档
|
||
|
||
> 文档编号: 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 {}
|
||
|
||
// 接口:使用PascalCase,I前缀可选
|
||
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. **开发流程**:分支管理、开发流程、发布流程
|
||
|
||
通过遵循本文档的开发规范,可以确保代码质量、提高开发效率、降低维护成本。
|