529 lines
11 KiB
Markdown
529 lines
11 KiB
Markdown
# 开发指南
|
||
|
||
## 开发环境设置
|
||
|
||
### 1. 环境要求
|
||
- **Node.js**: 20.x 或 22+
|
||
- **包管理器**: pnpm 9.x 或 10+
|
||
- **编辑器**: Visual Studio Code (推荐)
|
||
- **浏览器**: Chrome 或 Safari (移动端调试)
|
||
|
||
### 2. 推荐 VS Code 插件
|
||
- Vue Language Features (Volar)
|
||
- TypeScript Vue Plugin (Volar)
|
||
- ESLint
|
||
- UnoCSS
|
||
- Auto Rename Tag
|
||
- GitLens
|
||
|
||
### 3. 项目初始化
|
||
```bash
|
||
# 克隆项目
|
||
git clone <repository-url>
|
||
|
||
# 安装依赖
|
||
pnpm i
|
||
|
||
# 启动开发服务器
|
||
pnpm dev
|
||
```
|
||
|
||
## 项目架构
|
||
|
||
### 目录结构详解
|
||
|
||
```
|
||
src/
|
||
├── common/ # 通用模块
|
||
│ ├── apis/ # API 接口定义
|
||
│ │ ├── users/ # 用户相关接口
|
||
│ │ ├── shop/ # 店铺相关接口
|
||
│ │ ├── cabinet/ # 柜机相关接口
|
||
│ │ ├── approval/ # 审批相关接口
|
||
│ │ └── ab98/ # AB98系统接口
|
||
│ ├── assets/ # 静态资源
|
||
│ │ └── styles/ # 样式文件
|
||
│ ├── composables/ # 组合式函数
|
||
│ │ ├── useDark.ts # 暗黑模式
|
||
│ │ ├── useWatermark.ts # 水印功能
|
||
│ │ └── useGrayscaleAndColorblind.ts # 无障碍模式
|
||
│ ├── components/ # 通用组件
|
||
│ └── utils/ # 工具函数
|
||
│ ├── cache/ # 缓存工具
|
||
│ ├── permission.ts # 权限工具
|
||
│ └── wx.ts # 微信工具
|
||
├── layout/ # 布局组件
|
||
│ ├── index.vue # 主布局
|
||
│ ├── components/
|
||
│ │ ├── NavBar.vue # 导航栏
|
||
│ │ ├── Tabbar.vue # 标签栏
|
||
│ │ └── Footer.vue # 底部
|
||
├── pages/ # 页面组件
|
||
│ ├── product/ # 商品相关页面
|
||
│ ├── order/ # 订单相关页面
|
||
│ ├── cabinet/ # 柜机相关页面
|
||
│ ├── approval/ # 审批相关页面
|
||
│ ├── me/ # 个人中心
|
||
│ └── error/ # 错误页面
|
||
├── pinia/ # 状态管理
|
||
│ ├── index.ts # Pinia 实例
|
||
│ └── stores/ # Store 定义
|
||
│ ├── user.ts # 用户状态
|
||
│ ├── ab98-user.ts # AB98用户状态
|
||
│ ├── wx.ts # 微信状态
|
||
│ ├── product.ts # 商品状态
|
||
│ ├── cart.ts # 购物车状态
|
||
│ ├── order.ts # 订单状态
|
||
│ └── approval.ts # 审批状态
|
||
├── router/ # 路由配置
|
||
│ ├── index.ts # 路由定义
|
||
│ ├── guard.ts # 路由守卫
|
||
│ └── whitelist.ts # 白名单
|
||
├── http/ # HTTP 请求
|
||
│ └── axios.ts # Axios 配置
|
||
└── plugins/ # 插件配置
|
||
├── index.ts # 插件安装
|
||
├── permission-directive.ts # 权限指令
|
||
└── console.ts # 控制台工具
|
||
```
|
||
|
||
## 开发规范
|
||
|
||
### 1. 代码风格
|
||
|
||
#### TypeScript 规范
|
||
- 使用严格模式,避免 `any` 类型
|
||
- 为所有函数和变量提供明确的类型定义
|
||
- 使用接口定义复杂的数据结构
|
||
|
||
```typescript
|
||
// ✅ 推荐
|
||
interface UserInfo {
|
||
id: number;
|
||
name: string;
|
||
email: string;
|
||
}
|
||
|
||
const getUserInfo = async (id: number): Promise<UserInfo> => {
|
||
// ...
|
||
}
|
||
|
||
// ❌ 避免
|
||
const getUserInfo = async (id: any): Promise<any> => {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### Vue 组件规范
|
||
- 使用 script setup 语法
|
||
- 组件名使用 PascalCase
|
||
- Props 和 Emits 使用 TypeScript 定义
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
interface Props {
|
||
title: string;
|
||
count?: number;
|
||
}
|
||
|
||
interface Emits {
|
||
(e: 'update:count', value: number): void;
|
||
}
|
||
|
||
const props = defineProps<Props>();
|
||
const emit = defineEmits<Emits>();
|
||
|
||
const handleClick = () => {
|
||
emit('update:count', (props.count || 0) + 1);
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<div class="my-component">
|
||
<h3>{{ title }}</h3>
|
||
<button @click="handleClick">
|
||
点击 {{ count }}
|
||
</button>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
### 2. 状态管理规范
|
||
|
||
#### Store 定义
|
||
- Store 名使用 camelCase
|
||
- 使用组合式 API 风格
|
||
- 提供清晰的类型定义
|
||
|
||
```typescript
|
||
import { pinia } from "@/pinia";
|
||
|
||
export const useProductStore = defineStore("product", () => {
|
||
// State
|
||
const products = ref<Product[]>([]);
|
||
const loading = ref(false);
|
||
|
||
// Getters
|
||
const availableProducts = computed(() =>
|
||
products.value.filter(p => p.stock > 0)
|
||
);
|
||
|
||
// Actions
|
||
const fetchProducts = async () => {
|
||
loading.value = true;
|
||
try {
|
||
const response = await getProductListApi();
|
||
if (response.code === 0) {
|
||
products.value = response.data.products;
|
||
}
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
return {
|
||
products,
|
||
loading,
|
||
availableProducts,
|
||
fetchProducts
|
||
};
|
||
});
|
||
```
|
||
|
||
### 3. API 开发规范
|
||
|
||
#### API 文件结构
|
||
```typescript
|
||
// src/common/apis/product/index.ts
|
||
import { request } from "@/http/axios";
|
||
import type { ProductListResponse, ProductDetailResponse } from "./type";
|
||
|
||
export const getProductListApi = (params?: { page?: number; size?: number }) => {
|
||
return request<ProductListResponse>({
|
||
url: "/api/v1/product/list",
|
||
method: "GET",
|
||
params
|
||
});
|
||
};
|
||
|
||
export const getProductDetailApi = (id: number) => {
|
||
return request<ProductDetailResponse>({
|
||
url: `/api/v1/product/${id}`,
|
||
method: "GET"
|
||
});
|
||
};
|
||
```
|
||
|
||
#### 类型定义文件
|
||
```typescript
|
||
// src/common/apis/product/type.ts
|
||
export interface Product {
|
||
id: number;
|
||
name: string;
|
||
price: number;
|
||
image: string;
|
||
description: string;
|
||
stock: number;
|
||
category: string;
|
||
}
|
||
|
||
export interface ProductListResponse {
|
||
products: Product[];
|
||
total: number;
|
||
page: number;
|
||
size: number;
|
||
}
|
||
|
||
export interface ProductDetailResponse extends Product {
|
||
specifications: Specification[];
|
||
images: string[];
|
||
details: string;
|
||
}
|
||
```
|
||
|
||
### 4. 路由配置规范
|
||
|
||
#### 路由定义
|
||
```typescript
|
||
// src/router/index.ts
|
||
export const routes: RouteRecordRaw[] = [
|
||
{
|
||
path: "/product/:id",
|
||
component: () => import("@/pages/product/ProductDetail.vue"),
|
||
name: "ProductDetail",
|
||
meta: {
|
||
title: "商品详情",
|
||
keepAlive: true,
|
||
layout: {
|
||
navBar: {
|
||
showNavBar: true,
|
||
showLeftArrow: true
|
||
},
|
||
tabbar: {
|
||
showTabbar: false
|
||
}
|
||
},
|
||
requiresAuth: true
|
||
}
|
||
}
|
||
];
|
||
```
|
||
|
||
### 5. 样式开发规范
|
||
|
||
#### UnoCSS 使用
|
||
- 优先使用预设的原子类
|
||
- 自定义规则在 `uno.config.ts` 中定义
|
||
- 使用属性化模式(un-前缀)
|
||
|
||
```vue
|
||
<template>
|
||
<div class="product-card un-p-4 un-bg-white un-rounded-lg un-shadow-sm">
|
||
<img
|
||
:src="product.image"
|
||
class="un-w-full un-h-40 un-object-cover un-rounded"
|
||
/>
|
||
<h3 class="un-text-lg un-font-medium un-mt-2 un-text-gray-900">
|
||
{{ product.name }}
|
||
</h3>
|
||
<p class="un-text-primary un-font-bold un-mt-1">
|
||
¥{{ product.price }}
|
||
</p>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
#### CSS 变量
|
||
在 `src/common/assets/styles/variables.css` 中定义主题变量:
|
||
|
||
```css
|
||
:root {
|
||
--mobvue-primary-color: #1989fa;
|
||
--mobvue-bg-color: #ffffff;
|
||
--mobvue-text-color: #323233;
|
||
}
|
||
|
||
html.dark {
|
||
--mobvue-primary-color: #2d8cf0;
|
||
--mobvue-bg-color: #1a1a1a;
|
||
--mobvue-text-color: #e5e5e5;
|
||
}
|
||
```
|
||
|
||
## 功能开发指南
|
||
|
||
### 1. 添加新页面
|
||
|
||
#### 步骤 1: 创建页面组件
|
||
```vue
|
||
<!-- src/pages/example/ExamplePage.vue -->
|
||
<script setup lang="ts">
|
||
import { useRoute } from "vue-router";
|
||
|
||
const route = useRoute();
|
||
|
||
// 页面逻辑...
|
||
</script>
|
||
|
||
<template>
|
||
<div class="example-page">
|
||
<h1>示例页面</h1>
|
||
<!-- 页面内容 -->
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.example-page {
|
||
padding: 16px;
|
||
}
|
||
</style>
|
||
```
|
||
|
||
#### 步骤 2: 配置路由
|
||
```typescript
|
||
// src/router/index.ts
|
||
export const routes: RouteRecordRaw[] = [
|
||
{
|
||
path: "/example",
|
||
component: () => import("@/pages/example/ExamplePage.vue"),
|
||
name: "ExamplePage",
|
||
meta: {
|
||
title: "示例页面",
|
||
layout: {
|
||
navBar: {
|
||
showNavBar: true,
|
||
showLeftArrow: true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
];
|
||
```
|
||
|
||
### 2. 添加新 Store
|
||
|
||
```typescript
|
||
// src/pinia/stores/example.ts
|
||
import { pinia } from "@/pinia";
|
||
|
||
export const useExampleStore = defineStore("example", () => {
|
||
const data = ref<string[]>([]);
|
||
const loading = ref(false);
|
||
|
||
const fetchData = async () => {
|
||
loading.value = true;
|
||
try {
|
||
// API 调用...
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
return {
|
||
data,
|
||
loading,
|
||
fetchData
|
||
};
|
||
});
|
||
```
|
||
|
||
### 3. 添加新 API
|
||
|
||
```typescript
|
||
// src/common/apis/example/index.ts
|
||
import { request } from "@/http/axios";
|
||
import type { ExampleResponse } from "./type";
|
||
|
||
export const getExampleDataApi = () => {
|
||
return request<ExampleResponse>({
|
||
url: "/api/v1/example/data",
|
||
method: "GET"
|
||
});
|
||
};
|
||
|
||
// src/common/apis/example/type.ts
|
||
export interface ExampleResponse {
|
||
data: string[];
|
||
}
|
||
```
|
||
|
||
## 调试技巧
|
||
|
||
### 1. 移动端调试
|
||
- 使用 Chrome DevTools 的设备模拟
|
||
- 启用移动端触摸模拟
|
||
- 测试不同屏幕尺寸
|
||
|
||
### 2. 微信调试
|
||
- 使用微信开发者工具
|
||
- 配置正确的回调域名
|
||
- 检查 openid 获取逻辑
|
||
|
||
### 3. 状态调试
|
||
- 使用 Vue DevTools
|
||
- 查看 Pinia Store 状态
|
||
- 监控组件生命周期
|
||
|
||
## 性能优化
|
||
|
||
### 1. 代码分割
|
||
- 路由级别的懒加载
|
||
- 组件按需导入
|
||
- 第三方库独立打包
|
||
|
||
### 2. 图片优化
|
||
- 使用 WebP 格式
|
||
- 实现懒加载
|
||
- 压缩图片大小
|
||
|
||
### 3. 缓存策略
|
||
- 合理使用浏览器缓存
|
||
- API 响应缓存
|
||
- 组件 keep-alive
|
||
|
||
## 测试指南
|
||
|
||
### 1. 单元测试
|
||
```typescript
|
||
// tests/example.test.ts
|
||
import { describe, it, expect } from "vitest";
|
||
import { mount } from "@vue/test-utils";
|
||
import ExampleComponent from "@/components/ExampleComponent.vue";
|
||
|
||
describe("ExampleComponent", () => {
|
||
it("renders correctly", () => {
|
||
const wrapper = mount(ExampleComponent, {
|
||
props: {
|
||
title: "Test Title"
|
||
}
|
||
});
|
||
|
||
expect(wrapper.text()).toContain("Test Title");
|
||
});
|
||
});
|
||
```
|
||
|
||
### 2. E2E 测试
|
||
- 使用 Playwright 或 Cypress
|
||
- 模拟用户操作流程
|
||
- 测试关键业务路径
|
||
|
||
## 部署指南
|
||
|
||
### 1. 环境变量配置
|
||
```env
|
||
# .env.production
|
||
VITE_BASE_URL=https://api.example.com
|
||
VITE_PUBLIC_PATH=/
|
||
VITE_ROUTER_HISTORY=hash
|
||
```
|
||
|
||
### 2. 构建优化
|
||
```bash
|
||
# 生产环境构建
|
||
pnpm build
|
||
|
||
# 预发布环境构建
|
||
pnpm build:staging
|
||
```
|
||
|
||
### 3. 部署检查清单
|
||
- [ ] 环境变量配置正确
|
||
- [ ] 静态资源路径正确
|
||
- [ ] API 接口可访问
|
||
- [ ] 路由配置正确
|
||
- [ ] 移动端适配正常
|
||
|
||
## 常见问题解决
|
||
|
||
### 1. 微信认证失败
|
||
- 检查回调域名配置
|
||
- 验证 corpid 和 secret
|
||
- 查看网络请求日志
|
||
|
||
### 2. 路由跳转问题
|
||
- 检查路由守卫逻辑
|
||
- 验证权限配置
|
||
- 查看路由历史模式
|
||
|
||
### 3. 样式显示异常
|
||
- 检查 UnoCSS 配置
|
||
- 验证 CSS 变量定义
|
||
- 测试不同主题模式
|
||
|
||
## 贡献指南
|
||
|
||
### 1. 代码提交
|
||
- 遵循提交信息规范
|
||
- 一个功能一个提交
|
||
- 提供清晰的提交描述
|
||
|
||
### 2. 代码审查
|
||
- 检查代码规范
|
||
- 验证功能完整性
|
||
- 测试边界情况
|
||
|
||
### 3. 文档更新
|
||
- 更新相关文档
|
||
- 添加使用示例
|
||
- 记录变更内容 |