814 lines
18 KiB
Markdown
814 lines
18 KiB
Markdown
# Vben Admin Web-Antd AI 开发规则手册
|
||
|
||
## 项目概述
|
||
|
||
本项目基于 **Vben Admin 5.x** 框架开发,使用 Vue 3 + TypeScript + Ant Design Vue。项目采用 **monorepo** 结构,当前应用位于 `apps/web-antd`,依赖 `packages` 目录下的框架核心包。
|
||
|
||
### 技术栈
|
||
- **框架**: Vue 3.5+ (Composition API)
|
||
- **语言**: TypeScript 5.8+
|
||
- **UI 库**: Ant Design Vue 4.x
|
||
- **状态管理**: Pinia
|
||
- **路由**: Vue Router 4.x
|
||
- **构建工具**: Vite 6.x
|
||
- **包管理器**: pnpm (必须使用,不能用 npm/yarn)
|
||
- **图标**: Iconify + Lucide
|
||
|
||
---
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
apps/web-antd/
|
||
├── src/
|
||
│ ├── api/ # API 请求封装
|
||
│ │ ├── core/ # 核心 API (auth, menu, user)
|
||
│ │ ├── request.ts # 请求客户端配置
|
||
│ │ └── index.ts # 导出所有 API
|
||
│ ├── router/ # 路由配置
|
||
│ │ ├── routes/ # 路由定义
|
||
│ │ │ ├── core/ # 核心路由 (login, 404等)
|
||
│ │ │ └── modules/ # 业务路由模块
|
||
│ │ └── index.ts # 路由实例
|
||
│ ├── store/ # Pinia 状态管理
|
||
│ ├── views/ # 页面组件
|
||
│ │ ├── _core/ # 核心页面 (login, 404)
|
||
│ │ ├── dashboard/ # 仪表盘
|
||
│ │ └── demos/ # 示例页面
|
||
│ ├── layouts/ # 布局组件(如需自定义)
|
||
│ ├── locales/ # 国际化配置
|
||
│ ├── adapter/ # 组件适配器
|
||
│ ├── app.vue # 根组件
|
||
│ ├── main.ts # 入口文件
|
||
│ └── preferences.ts # 应用偏好设置
|
||
├── package.json
|
||
└── vite.config.ts
|
||
```
|
||
|
||
---
|
||
|
||
## 核心开发规范
|
||
|
||
### 1. 命名规范
|
||
|
||
#### 文件命名
|
||
- **组件文件**: PascalCase,如 `UserProfile.vue`, `DataTable.vue`
|
||
- **工具文件**: kebab-case,如 `format-date.ts`, `use-table.ts`
|
||
- **API 文件**: kebab-case,如 `user-api.ts`, `order-api.ts`
|
||
- **路由文件**: kebab-case,如 `user-management.ts`
|
||
|
||
#### 变量命名
|
||
- **组件变量**: PascalCase,如 `const UserTable = defineComponent(...)`
|
||
- **普通变量**: camelCase,如 `const userInfo = ref({})`
|
||
- **常量**: UPPER_SNAKE_CASE,如 `const API_BASE_URL = '...'`
|
||
- **类型/接口**: PascalCase,如 `interface UserInfo { ... }`
|
||
|
||
### 2. Vue 组件规范
|
||
|
||
**必须使用 Composition API + `<script setup>`**:
|
||
|
||
```vue
|
||
<script lang="ts" setup>
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import type { UserInfo } from '#/api/types';
|
||
|
||
// 定义组件名(可选,但推荐)
|
||
defineOptions({ name: 'UserProfile' });
|
||
|
||
// Props 定义
|
||
interface Props {
|
||
userId: string;
|
||
showActions?: boolean;
|
||
}
|
||
|
||
const props = withDefaults(defineProps<Props>(), {
|
||
showActions: true,
|
||
});
|
||
|
||
// Emits 定义
|
||
const emit = defineEmits<{
|
||
update: [user: UserInfo];
|
||
delete: [id: string];
|
||
}>();
|
||
|
||
// 响应式数据
|
||
const loading = ref(false);
|
||
const userInfo = ref<UserInfo | null>(null);
|
||
|
||
// 计算属性
|
||
const displayName = computed(() => {
|
||
return userInfo.value?.name || 'Unknown';
|
||
});
|
||
|
||
// 方法
|
||
async function fetchUser() {
|
||
loading.value = true;
|
||
try {
|
||
const data = await getUserApi(props.userId);
|
||
userInfo.value = data;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
// 生命周期
|
||
onMounted(() => {
|
||
fetchUser();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="user-profile">
|
||
<a-spin :spinning="loading">
|
||
<h3>{{ displayName }}</h3>
|
||
</a-spin>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.user-profile {
|
||
padding: 16px;
|
||
}
|
||
</style>
|
||
```
|
||
|
||
### 3. 导入路径别名
|
||
|
||
使用 `#/` 别名指向 `src/` 目录:
|
||
|
||
```typescript
|
||
// ✅ 正确
|
||
import { useAuthStore } from '#/store';
|
||
import type { UserInfo } from '#/api/types';
|
||
import { $t } from '#/locales';
|
||
|
||
// ❌ 错误 - 不要使用相对路径
|
||
import { useAuthStore } from '../../../store';
|
||
```
|
||
|
||
### 4. 框架组件导入
|
||
|
||
框架提供的组件从 `@vben/*` 包导入:
|
||
|
||
```typescript
|
||
// UI 组件
|
||
import { Button, Modal, Drawer } from '@vben/common-ui';
|
||
import { Page, Card } from '@vben/common-ui';
|
||
import { VbenForm, VbenTable } from '@vben/common-ui';
|
||
|
||
// 布局组件
|
||
import { BasicLayout, PageWrapper } from '@vben/layouts';
|
||
|
||
// 图标
|
||
import { SvgIcon, IconSvg } from '@vben/icons';
|
||
|
||
// Hooks
|
||
import { usePreferences, useAppConfig } from '@vben/hooks';
|
||
|
||
// 工具函数
|
||
import { formatDate, formatMoney } from '@vben/utils';
|
||
|
||
// 权限
|
||
import { useAccess } from '@vben/access';
|
||
|
||
// Store
|
||
import { useAccessStore, useUserStore } from '@vben/stores';
|
||
|
||
// 请求
|
||
import { RequestClient } from '@vben/request';
|
||
```
|
||
|
||
---
|
||
|
||
## 开发模式详解
|
||
|
||
### 1. 创建新页面
|
||
|
||
#### 步骤 1: 创建视图组件
|
||
|
||
在 `src/views/` 下创建页面组件:
|
||
|
||
```vue
|
||
<!-- src/views/user/user-list.vue -->
|
||
<script lang="ts" setup>
|
||
import { ref, onMounted } from 'vue';
|
||
import { VbenTable } from '@vben/common-ui';
|
||
import type { VbenTableColumn } from '@vben/common-ui';
|
||
import { getUserListApi } from '#/api/user';
|
||
|
||
defineOptions({ name: 'UserList' });
|
||
|
||
const loading = ref(false);
|
||
const dataSource = ref([]);
|
||
|
||
const columns: VbenTableColumn[] = [
|
||
{ title: '用户名', dataIndex: 'username', key: 'username' },
|
||
{ title: '邮箱', dataIndex: 'email', key: 'email' },
|
||
{ title: '状态', dataIndex: 'status', key: 'status' },
|
||
];
|
||
|
||
async function fetchData() {
|
||
loading.value = true;
|
||
try {
|
||
const data = await getUserListApi();
|
||
dataSource.value = data;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
fetchData();
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="p-5">
|
||
<VbenTable
|
||
:columns="columns"
|
||
:data-source="dataSource"
|
||
:loading="loading"
|
||
/>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
#### 步骤 2: 添加路由配置
|
||
|
||
在 `src/router/routes/modules/` 下创建路由模块:
|
||
|
||
```typescript
|
||
// src/router/routes/modules/user.ts
|
||
import type { RouteRecordRaw } from 'vue-router';
|
||
import { $t } from '#/locales';
|
||
|
||
const routes: RouteRecordRaw[] = [
|
||
{
|
||
meta: {
|
||
icon: 'lucide:users',
|
||
title: '用户管理',
|
||
order: 10, // 菜单排序
|
||
},
|
||
name: 'User',
|
||
path: '/user',
|
||
children: [
|
||
{
|
||
name: 'UserList',
|
||
path: '/user/list',
|
||
component: () => import('#/views/user/user-list.vue'),
|
||
meta: {
|
||
icon: 'lucide:user-cog',
|
||
title: '用户列表',
|
||
},
|
||
},
|
||
{
|
||
name: 'UserDetail',
|
||
path: '/user/detail/:id',
|
||
component: () => import('#/views/user/user-detail.vue'),
|
||
meta: {
|
||
hideInMenu: true, // 不在菜单中显示
|
||
title: '用户详情',
|
||
activeMenu: '/user/list', // 激活父菜单
|
||
},
|
||
},
|
||
],
|
||
},
|
||
];
|
||
|
||
export default routes;
|
||
```
|
||
|
||
**路由配置要点:**
|
||
- 必须 `export default routes`
|
||
- `component` 使用动态导入 `() => import(...)`
|
||
- 使用 `#/views/` 别名
|
||
- 图标使用 Iconify 格式,如 `lucide:users`
|
||
- `meta.order` 控制菜单顺序(数字越小越靠前)
|
||
|
||
#### 步骤 3: 添加国际化(可选)
|
||
|
||
```typescript
|
||
// src/locales/lang/zh-CN/user.ts
|
||
export default {
|
||
userManagement: '用户管理',
|
||
userList: '用户列表',
|
||
userName: '用户名',
|
||
email: '邮箱',
|
||
};
|
||
```
|
||
|
||
### 2. API 请求规范
|
||
|
||
#### 在 `src/api/` 下创建 API 模块:
|
||
|
||
```typescript
|
||
// src/api/user.ts
|
||
import { requestClient } from './request';
|
||
|
||
export interface UserInfo {
|
||
id: string;
|
||
username: string;
|
||
email: string;
|
||
status: 'active' | 'inactive';
|
||
}
|
||
|
||
export interface UserListParams {
|
||
page?: number;
|
||
pageSize?: number;
|
||
keyword?: string;
|
||
}
|
||
|
||
/**
|
||
* 获取用户列表
|
||
*/
|
||
export async function getUserListApi(params?: UserListParams) {
|
||
return requestClient.get<UserInfo[]>('/user/list', { params });
|
||
}
|
||
|
||
/**
|
||
* 获取用户详情
|
||
*/
|
||
export async function getUserDetailApi(id: string) {
|
||
return requestClient.get<UserInfo>(`/user/${id}`);
|
||
}
|
||
|
||
/**
|
||
* 创建用户
|
||
*/
|
||
export async function createUserApi(data: Partial<UserInfo>) {
|
||
return requestClient.post<UserInfo>('/user', data);
|
||
}
|
||
|
||
/**
|
||
* 更新用户
|
||
*/
|
||
export async function updateUserApi(id: string, data: Partial<UserInfo>) {
|
||
return requestClient.put<UserInfo>(`/user/${id}`, data);
|
||
}
|
||
|
||
/**
|
||
* 删除用户
|
||
*/
|
||
export async function deleteUserApi(id: string) {
|
||
return requestClient.delete(`/user/${id}`);
|
||
}
|
||
```
|
||
|
||
**API 规范:**
|
||
- 使用 `requestClient` 发起请求(已配置拦截器、token、错误处理)
|
||
- 函数名以 `Api` 结尾
|
||
- 使用 TypeScript 定义请求参数和返回类型
|
||
- 添加 JSDoc 注释说明用途
|
||
|
||
### 3. 状态管理 (Pinia)
|
||
|
||
#### 创建 Store:
|
||
|
||
```typescript
|
||
// src/store/modules/user.ts
|
||
import { defineStore } from 'pinia';
|
||
import { ref, computed } from 'vue';
|
||
import type { UserInfo } from '#/api/types';
|
||
import { getUserDetailApi } from '#/api/user';
|
||
|
||
export const useUserStore = defineStore('user', () => {
|
||
// State
|
||
const userInfo = ref<UserInfo | null>(null);
|
||
const loading = ref(false);
|
||
|
||
// Getters
|
||
const isLoggedIn = computed(() => !!userInfo.value);
|
||
const userName = computed(() => userInfo.value?.username || 'Guest');
|
||
|
||
// Actions
|
||
async function fetchUserInfo() {
|
||
loading.value = true;
|
||
try {
|
||
const data = await getUserDetailApi('me');
|
||
userInfo.value = data;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
function clearUser() {
|
||
userInfo.value = null;
|
||
}
|
||
|
||
return {
|
||
// State
|
||
userInfo,
|
||
loading,
|
||
// Getters
|
||
isLoggedIn,
|
||
userName,
|
||
// Actions
|
||
fetchUserInfo,
|
||
clearUser,
|
||
};
|
||
});
|
||
```
|
||
|
||
**使用 Store:**
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { useUserStore } from '#/store';
|
||
|
||
const userStore = useUserStore();
|
||
|
||
// 读取状态
|
||
console.log(userStore.userName);
|
||
|
||
// 调用方法
|
||
userStore.fetchUserInfo();
|
||
</script>
|
||
```
|
||
|
||
### 4. 使用框架表单组件 (VbenForm)
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue';
|
||
import { VbenForm } from '@vben/common-ui';
|
||
import type { VbenFormSchema } from '@vben/common-ui';
|
||
import { message } from 'ant-design-vue';
|
||
|
||
const formSchema: VbenFormSchema[] = [
|
||
{
|
||
component: 'Input',
|
||
label: '用户名',
|
||
fieldName: 'username',
|
||
required: true,
|
||
rules: [{ required: true, message: '请输入用户名' }],
|
||
},
|
||
{
|
||
component: 'Input',
|
||
label: '邮箱',
|
||
fieldName: 'email',
|
||
required: true,
|
||
componentProps: {
|
||
type: 'email',
|
||
},
|
||
rules: [
|
||
{ required: true, message: '请输入邮箱' },
|
||
{ type: 'email', message: '邮箱格式不正确' },
|
||
],
|
||
},
|
||
{
|
||
component: 'Select',
|
||
label: '状态',
|
||
fieldName: 'status',
|
||
componentProps: {
|
||
options: [
|
||
{ label: '启用', value: 'active' },
|
||
{ label: '禁用', value: 'inactive' },
|
||
],
|
||
},
|
||
},
|
||
{
|
||
component: 'DatePicker',
|
||
label: '出生日期',
|
||
fieldName: 'birthday',
|
||
},
|
||
];
|
||
|
||
const formValue = ref({});
|
||
|
||
async function handleSubmit(values: any) {
|
||
console.log('提交数据:', values);
|
||
message.success('保存成功');
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="p-5">
|
||
<VbenForm
|
||
v-model="formValue"
|
||
:schema="formSchema"
|
||
@submit="handleSubmit"
|
||
/>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
### 5. 使用框架表格组件 (VbenTable)
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue';
|
||
import { VbenTable } from '@vben/common-ui';
|
||
import type { VbenTableColumn } from '@vben/common-ui';
|
||
import { Button, Tag } from 'ant-design-vue';
|
||
|
||
const columns: VbenTableColumn[] = [
|
||
{
|
||
title: '用户名',
|
||
dataIndex: 'username',
|
||
key: 'username',
|
||
width: 200,
|
||
},
|
||
{
|
||
title: '邮箱',
|
||
dataIndex: 'email',
|
||
key: 'email',
|
||
},
|
||
{
|
||
title: '状态',
|
||
dataIndex: 'status',
|
||
key: 'status',
|
||
width: 100,
|
||
customRender: ({ text }) => {
|
||
const color = text === 'active' ? 'green' : 'red';
|
||
return <Tag color={color}>{text}</Tag>;
|
||
},
|
||
},
|
||
{
|
||
title: '操作',
|
||
key: 'action',
|
||
width: 200,
|
||
customRender: ({ record }) => (
|
||
<>
|
||
<Button type="link" onClick={() => handleEdit(record)}>编辑</Button>
|
||
<Button type="link" danger onClick={() => handleDelete(record.id)}>删除</Button>
|
||
</>
|
||
),
|
||
},
|
||
];
|
||
|
||
const dataSource = ref([
|
||
{ id: '1', username: 'admin', email: 'admin@example.com', status: 'active' },
|
||
{ id: '2', username: 'user', email: 'user@example.com', status: 'inactive' },
|
||
]);
|
||
|
||
function handleEdit(record: any) {
|
||
console.log('编辑:', record);
|
||
}
|
||
|
||
function handleDelete(id: string) {
|
||
console.log('删除:', id);
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<VbenTable
|
||
:columns="columns"
|
||
:data-source="dataSource"
|
||
:pagination="{ pageSize: 10 }"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
### 6. 权限控制
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { useAccess } from '@vben/access';
|
||
|
||
const { hasAccessByCodes } = useAccess();
|
||
|
||
// 检查权限
|
||
const canEdit = hasAccessByCodes(['user:edit']);
|
||
const canDelete = hasAccessByCodes(['user:delete']);
|
||
</script>
|
||
|
||
<template>
|
||
<div>
|
||
<!-- 使用 v-access 指令控制显示 -->
|
||
<a-button v-access="'user:create'">创建用户</a-button>
|
||
<a-button v-access="'user:edit'" v-if="canEdit">编辑</a-button>
|
||
<a-button v-access="'user:delete'" v-if="canDelete">删除</a-button>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
### 7. 使用图标
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { IconSvg } from '@vben/icons';
|
||
import { SvgUserIcon, SvgSettingsIcon } from '@vben/icons';
|
||
</script>
|
||
|
||
<template>
|
||
<div>
|
||
<!-- 方式 1: 使用 IconSvg 组件 + Iconify 名称 -->
|
||
<IconSvg icon="lucide:user" class="text-xl" />
|
||
<IconSvg icon="mdi:settings" :size="24" />
|
||
|
||
<!-- 方式 2: 使用预定义的 SVG 组件 -->
|
||
<SvgUserIcon class="text-xl" />
|
||
<SvgSettingsIcon :size="24" />
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
**常用图标集:**
|
||
- `lucide:*` - Lucide 图标(推荐)
|
||
- `mdi:*` - Material Design Icons
|
||
- `carbon:*` - Carbon Icons
|
||
- `heroicons:*` - Heroicons
|
||
|
||
浏览图标:https://icon-sets.iconify.design/
|
||
|
||
---
|
||
|
||
## 样式规范
|
||
|
||
### 1. 使用 Tailwind CSS
|
||
|
||
优先使用 Tailwind CSS 工具类:
|
||
|
||
```vue
|
||
<template>
|
||
<div class="p-5 bg-white rounded-lg shadow-md">
|
||
<h2 class="text-2xl font-bold mb-4">标题</h2>
|
||
<div class="flex items-center gap-4">
|
||
<a-button type="primary">按钮</a-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
### 2. Scoped 样式
|
||
|
||
需要自定义样式时使用 `<style scoped>`:
|
||
|
||
```vue
|
||
<style scoped>
|
||
.custom-class {
|
||
/* 自定义样式 */
|
||
}
|
||
|
||
/* 深度选择器 - 影响子组件 */
|
||
:deep(.ant-btn) {
|
||
border-radius: 8px;
|
||
}
|
||
|
||
/* 插槽选择器 */
|
||
:slotted(.slot-content) {
|
||
color: red;
|
||
}
|
||
</style>
|
||
```
|
||
|
||
### 3. 响应式设计
|
||
|
||
使用 Tailwind 响应式前缀:
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 移动端单列,平板双列,桌面三列 -->
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
<div v-for="item in items" :key="item.id">
|
||
{{ item.name }}
|
||
</div>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
---
|
||
|
||
## 常见问题和注意事项
|
||
|
||
### 1. ❌ 不要做的事
|
||
|
||
- **不要使用 Options API**(必须用 Composition API)
|
||
- **不要使用相对路径导入**(使用 `#/` 别名)
|
||
- **不要直接修改 `packages/` 下的框架代码**
|
||
- **不要在组件中直接操作 DOM**(使用 Vue 的响应式系统)
|
||
- **不要使用 yarn/npm**(必须用 pnpm)
|
||
- **不要在路由配置中使用 `require`**(使用 `import()`)
|
||
|
||
### 2. ✅ 最佳实践
|
||
|
||
- **组件拆分**: 单个组件不超过 300 行,复杂组件拆分成子组件
|
||
- **类型安全**: 所有 API 响应、组件 Props、函数参数都定义 TypeScript 类型
|
||
- **错误处理**: 使用 `try-catch` 处理异步错误
|
||
- **加载状态**: 异步操作要有 loading 状态
|
||
- **国际化**: 所有用户可见的文本使用 `$t()` 函数
|
||
- **权限控制**: 敏感操作添加权限检查
|
||
|
||
### 3. 性能优化
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { computed, ref } from 'vue';
|
||
|
||
// ✅ 使用 computed 缓存计算结果
|
||
const filteredList = computed(() => {
|
||
return list.value.filter(item => item.status === 'active');
|
||
});
|
||
|
||
// ✅ 大列表使用虚拟滚动
|
||
import { VirtualList } from '@vben/common-ui';
|
||
</script>
|
||
|
||
<template>
|
||
<!-- ✅ 使用 v-show 而不是 v-if(频繁切换的元素) -->
|
||
<div v-show="visible">内容</div>
|
||
|
||
<!-- ✅ 长列表使用 key -->
|
||
<div v-for="item in list" :key="item.id">
|
||
{{ item.name }}
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
### 4. 调试技巧
|
||
|
||
```typescript
|
||
// 开发环境打印日志
|
||
if (import.meta.env.DEV) {
|
||
console.log('调试信息:', data);
|
||
}
|
||
|
||
// 使用 Vue Devtools
|
||
// Chrome 扩展:Vue.js devtools
|
||
```
|
||
|
||
---
|
||
|
||
## 开发命令
|
||
|
||
```bash
|
||
# 启动开发服务器
|
||
pnpm run dev:antd
|
||
|
||
# 构建生产版本
|
||
pnpm run build:antd
|
||
|
||
# 类型检查
|
||
pnpm run check:type
|
||
|
||
# 代码格式化
|
||
pnpm run format
|
||
|
||
# 代码检查
|
||
pnpm run lint
|
||
|
||
# 单元测试
|
||
pnpm run test:unit
|
||
```
|
||
|
||
---
|
||
|
||
## API Mock 数据(开发阶段)
|
||
|
||
如果后端接口未就绪,可以使用 Mock 数据:
|
||
|
||
```typescript
|
||
// src/api/user.ts
|
||
import { requestClient } from './request';
|
||
|
||
export async function getUserListApi() {
|
||
// 开发环境使用 mock 数据
|
||
if (import.meta.env.DEV) {
|
||
return Promise.resolve([
|
||
{ id: '1', username: 'admin', email: 'admin@example.com', status: 'active' },
|
||
{ id: '2', username: 'user', email: 'user@example.com', status: 'inactive' },
|
||
]);
|
||
}
|
||
|
||
// 生产环境调用真实 API
|
||
return requestClient.get('/user/list');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 环境变量配置
|
||
|
||
配置文件位置:
|
||
- `.env` - 所有环境
|
||
- `.env.development` - 开发环境
|
||
- `.env.production` - 生产环境
|
||
|
||
```bash
|
||
# .env.development
|
||
VITE_APP_TITLE=DevOps 管理系统
|
||
VITE_API_URL=http://localhost:8080/api
|
||
```
|
||
|
||
使用方式:
|
||
|
||
```typescript
|
||
import { useAppConfig } from '@vben/hooks';
|
||
|
||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||
console.log('API地址:', apiURL);
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
遵循本手册的规范,可以确保:
|
||
1. ✅ 代码风格统一,易于维护
|
||
2. ✅ 充分利用框架能力,避免重复造轮子
|
||
3. ✅ 类型安全,减少运行时错误
|
||
4. ✅ 性能优化,用户体验良好
|
||
5. ✅ 团队协作顺畅,代码可读性强
|
||
|
||
**开发新功能前,请先参考 `playground` 和 `demos` 目录下的示例代码!**
|