# 开发指南

## 开发环境搭建

### 1. 环境准备

```bash
# 安装 Node.js (版本 16.0+)
# 下载地址: https://nodejs.org/

# 安装 pnpm
npm install -g pnpm

# 验证安装
node --version
pnpm --version
```

### 2. 项目初始化

```bash
# 克隆项目
git clone <repository-url>
cd shop-front-end

# 安装依赖
pnpm install

# 启动开发服务器
pnpm dev
```

### 3. 开发工具配置

推荐使用 VSCode 并安装以下插件：
- Vue Language Features (Volar)
- TypeScript Vue Plugin (Volar)
- ESLint
- Prettier
- Stylelint

## 代码规范

### 1. 命名规范

#### 文件命名
- 组件文件: `PascalCase.vue`
- 工具文件: `camelCase.ts`
- 配置文件: `kebab-case.ts`
- 样式文件: `kebab-case.scss`

#### 变量命名
```typescript
// 正确
const userName = '张三'
const MAX_COUNT = 100
const userList = []

// 避免
const username = '张三'  // 驼峰
const max_count = 100   // 下划线
```

#### 组件命名
```vue
<!-- 正确 -->
<template>
  <UserProfile />
  <GoodsList />
</template>

<!-- 避免 -->
<template>
  <user-profile />
  <goods-list />
</template>
```

### 2. 代码结构

#### Vue 组件结构
```vue
<template>
  <!-- 模板内容 -->
</template>

<script setup lang="ts">
// 导入
import { ref, onMounted } from 'vue'
import { getUserInfo } from '@/api/user'

// 类型定义
interface User {
  id: number
  name: string
}

// 响应式数据
const userInfo = ref<User>()
const loading = ref(false)

// 方法
const fetchUser = async () => {
  loading.value = true
  try {
    const { data } = await getUserInfo()
    userInfo.value = data
  } catch (error) {
    console.error(error)
  } finally {
    loading.value = false
  }
}

// 生命周期
onMounted(() => {
  fetchUser()
})
</script>

<style scoped lang="scss">
/* 样式 */
</style>
```

### 3. TypeScript 规范

#### 类型定义
```typescript
// 接口定义
interface Goods {
  id: number
  name: string
  price: number
  stock: number
}

// 类型别名
type GoodsStatus = 'available' | 'sold_out' | 'disabled'

// 枚举
enum OrderStatus {
  PENDING = 1,
  PROCESSING = 2,
  COMPLETED = 3
}
```

#### 函数定义
```typescript
// 带类型的函数
const formatPrice = (price: number): string => {
  return `¥${price.toFixed(2)}`
}

// 异步函数
const fetchGoodsList = async (params: SearchParams): Promise<Goods[]> => {
  const { data } = await getGoodsList(params)
  return data
}
```

## API 开发规范

### 1. API 文件结构

```typescript
// src/api/shop/goods.ts
import request from '@/utils/request'

export interface Goods {
  id: number
  name: string
  price: number
  stock: number
}

export interface GoodsParams {
  page?: number
  size?: number
  keyword?: string
}

export interface GoodsListResponse {
  list: Goods[]
  total: number
}

// 获取商品列表
export const getGoodsList = (params: GoodsParams) => {
  return request.get<GoodsListResponse>('/api/goods/list', { params })
}

// 创建商品
export const createGoods = (data: Partial<Goods>) => {
  return request.post<Goods>('/api/goods', data)
}

// 更新商品
export const updateGoods = (id: number, data: Partial<Goods>) => {
  return request.put<Goods>(`/api/goods/${id}`, data)
}

// 删除商品
export const deleteGoods = (id: number) => {
  return request.delete(`/api/goods/${id}`)
}
```

### 2. 请求封装

```typescript
// src/utils/request.ts
import axios from 'axios'
import { ElMessage } from 'element-plus'

const request = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 10000
})

// 请求拦截器
request.interceptors.request.use(
  config => {
    // 添加 token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 响应拦截器
request.interceptors.response.use(
  response => {
    return response.data
  },
  error => {
    // 统一错误处理
    ElMessage.error(error.response?.data?.message || '请求失败')
    return Promise.reject(error)
  }
)

export default request
```

## 组件开发规范

### 1. 组件设计原则

#### 单一职责
每个组件只负责一个特定的功能。

#### 可复用性
组件应该设计为可复用的，通过 props 接收配置。

#### 组合优于继承
通过组合多个小组件来构建复杂功能。

### 2. Props 定义

```vue
<script setup lang="ts">
interface Props {
  // 必填属性
  title: string
  // 可选属性
  size?: 'small' | 'medium' | 'large'
  // 带默认值
  disabled?: boolean
  // 复杂对象
  data?: Record<string, any>
}

// 定义 props
const props = withDefaults(defineProps<Props>(), {
  size: 'medium',
  disabled: false,
  data: () => ({})
})
</script>
```

### 3. Emits 定义

```vue
<script setup lang="ts">
interface Emits {
  (e: 'update:modelValue', value: string): void
  (e: 'submit', data: Record<string, any>): void
  (e: 'cancel'): void
}

// 定义 emits
const emit = defineEmits<Emits>()

const handleSubmit = () => {
  emit('submit', formData)
}

const handleCancel = () => {
  emit('cancel')
}
</script>
```

## 状态管理规范

### 1. Store 定义

```typescript
// src/store/modules/user.ts
import { defineStore } from 'pinia'

export interface UserState {
  userInfo: User | null
  token: string | null
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    userInfo: null,
    token: null
  }),

  getters: {
    isLogin: state => !!state.token,
    userName: state => state.userInfo?.name || ''
  },

  actions: {
    setUserInfo(userInfo: User) {
      this.userInfo = userInfo
    },

    setToken(token: string) {
      this.token = token
    },

    async login(credentials: LoginParams) {
      const { data } = await loginApi(credentials)
      this.setToken(data.token)
      this.setUserInfo(data.user)
    },

    logout() {
      this.userInfo = null
      this.token = null
    }
  },

  persist: {
    enabled: true,
    strategies: [
      {
        key: 'user',
        storage: localStorage
      }
    ]
  }
})
```

### 2. Store 使用

```vue
<script setup lang="ts">
import { useUserStore } from '@/store/modules/user'

const userStore = useUserStore()

// 使用状态
const userName = computed(() => userStore.userName)
const isLogin = computed(() => userStore.isLogin)

// 调用 action
const handleLogin = async () => {
  try {
    await userStore.login(loginForm)
    // 登录成功处理
  } catch (error) {
    // 错误处理
  }
}
</script>
```

## 路由开发规范

### 1. 路由定义

```typescript
// src/router/modules/shop.ts
export default {
  path: '/shop',
  name: 'Shop',
  redirect: '/shop/goods',
  meta: {
    title: '店铺管理',
    icon: 'shop',
    rank: 10
  },
  children: [
    {
      path: '/shop/goods',
      name: 'Goods',
      component: () => import('@/views/shop/goods/index.vue'),
      meta: {
        title: '商品管理',
        roles: ['admin', 'shop_manager']
      }
    },
    {
      path: '/shop/orders',
      name: 'Orders',
      component: () => import('@/views/shop/orders/index.vue'),
      meta: {
        title: '订单管理',
        roles: ['admin', 'shop_manager']
      }
    }
  ]
} as RouteConfigsTable
```

### 2. 路由守卫

```typescript
// 在路由守卫中检查权限
router.beforeEach((to, from, next) => {
  const userStore = useUserStore()

  // 检查登录状态
  if (!userStore.isLogin && to.path !== '/login') {
    next('/login')
    return
  }

  // 检查页面权限
  if (to.meta.roles && !hasPermission(to.meta.roles)) {
    next('/error/403')
    return
  }

  next()
})
```

## 样式开发规范

### 1. SCSS 规范

```scss
// 变量定义
$primary-color: #409eff;
$success-color: #67c23a;
$warning-color: #e6a23c;
$danger-color: #f56c6c;

// 混入
@mixin flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

// 组件样式
.goods-card {
  border: 1px solid #ebeef5;
  border-radius: 4px;
  padding: 20px;

  &__header {
    @include flex-center;
    margin-bottom: 16px;
  }

  &__title {
    font-size: 16px;
    font-weight: bold;
    color: $primary-color;
  }
}
```

### 2. CSS 类命名

使用 BEM 命名规范：
```scss
.block {}
.block__element {}
.block--modifier {}
```

## 测试规范

### 1. 单元测试

```typescript
// tests/unit/utils/format.spec.ts
import { formatPrice } from '@/utils/format'

describe('formatPrice', () => {
  it('should format price correctly', () => {
    expect(formatPrice(100)).toBe('¥100.00')
    expect(formatPrice(99.9)).toBe('¥99.90')
  })
})
```

### 2. 组件测试

```typescript
// tests/unit/components/GoodsCard.spec.ts
import { mount } from '@vue/test-utils'
import GoodsCard from '@/components/GoodsCard.vue'

describe('GoodsCard', () => {
  it('should render goods info', () => {
    const wrapper = mount(GoodsCard, {
      props: {
        goods: {
          id: 1,
          name: '测试商品',
          price: 100
        }
      }
    })

    expect(wrapper.text()).toContain('测试商品')
    expect(wrapper.text()).toContain('¥100.00')
  })
})
```

## 提交规范

### 1. Commit Message 格式

```
<type>(<scope>): <subject>

<body>

<footer>
```

### 2. 类型说明

- `feat`: 新功能
- `fix`: 修复 bug
- `docs`: 文档更新
- `style`: 代码格式调整
- `refactor`: 代码重构
- `test`: 测试相关
- `chore`: 构建过程或辅助工具变动

### 3. 示例

```
feat(shop): 添加商品批量导入功能

- 支持 Excel 文件导入商品
- 添加导入进度显示
- 优化导入错误处理

Closes #123
```

## 性能优化

### 1. 代码分割

```typescript
// 路由懒加载
const GoodsList = () => import('@/views/shop/goods/index.vue')
```

### 2. 组件优化

```vue
<script setup lang="ts">
// 使用 defineAsyncComponent 异步加载组件
const AsyncComponent = defineAsyncComponent(() =>
  import('@/components/HeavyComponent.vue')
)

// 使用 computed 缓存计算结果
const filteredList = computed(() => {
  return list.value.filter(item => item.status === 'active')
})
</script>
```

### 3. 图片优化

```vue
<template>
  <!-- 使用懒加载 -->
  <el-image
    :src="imageUrl"
    lazy
    :preview-src-list="[imageUrl]"
  />
</template>
```

## 常见问题

### 1. 类型错误

如果遇到 TypeScript 类型错误，检查：
- 是否正确导入类型定义
- 是否正确定义接口
- 是否使用正确的类型注解

### 2. 样式不生效

检查：
- 是否使用了正确的 scoped
- 是否正确定义了样式类名
- 是否引入了必要的样式文件

### 3. 路由跳转问题

检查：
- 路由路径是否正确
- 路由守卫是否阻止了跳转
- 组件是否正确定义

---

**文档版本**: 1.0
**最后更新**: 2025-10-15