---
description: Vue 3 前端架构规范和组件开发
globs:
  - "frontend/**/*.vue"
  - "frontend/**/*.ts"
alwaysApply: false
---

# 前端架构规范

## 组件结构

### 目录组织

- 页面组件放在 `views/` 目录下，按模块组织
- 公共组件放在 `components/` 目录下
- 组件命名使用 PascalCase

### 组件语法

使用 Vue 3 Composition API 的 `<script setup lang="ts">` 语法：

```vue
<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { message } from "ant-design-vue";
import { getUsers, type User } from "@/api/users";

const loading = ref(false);
const users = ref<User[]>([]);

const activeUsers = computed(() =>
  users.value.filter((u) => u.validState === 1)
);

onMounted(async () => {
  await fetchUsers();
});

const fetchUsers = async () => {
  try {
    loading.value = true;
    users.value = await getUsers();
  } catch (error) {
    message.error("获取用户列表失败");
  } finally {
    loading.value = false;
  }
};
</script>

<template>
  <div class="p-4">
    <a-spin :spinning="loading">
      <a-table :dataSource="activeUsers" />
    </a-spin>
  </div>
</template>
```

## API 调用规范

### 目录结构

所有 API 调用放在 `api/` 目录下，按模块组织：

```
api/
├── users.ts
├── roles.ts
├── contests.ts
└── auth.ts
```

### API 函数命名

- `getXxx` - 获取数据
- `createXxx` - 创建数据
- `updateXxx` - 更新数据
- `deleteXxx` - 删除数据

### 示例

```typescript
// api/users.ts
import request from "@/utils/request";

export interface User {
  id: number;
  username: string;
  nickname: string;
  email?: string;
  validState: number;
}

export interface CreateUserDto {
  username: string;
  password: string;
  nickname: string;
  email?: string;
  roleIds?: number[];
}

export const getUsers = (params?: { skip?: number; take?: number }) => {
  return request.get<User[]>("/users", { params });
};

export const createUser = (data: CreateUserDto) => {
  return request.post<User>("/users", data);
};

export const updateUser = (id: number, data: Partial<CreateUserDto>) => {
  return request.put<User>(`/users/${id}`, data);
};

export const deleteUser = (id: number) => {
  return request.delete(`/users/${id}`);
};
```

## 状态管理 (Pinia)

### Store 规范

- Store 文件放在 `stores/` 目录下
- 使用 `defineStore()` 定义 store
- Store 命名使用 camelCase + Store 后缀

### 示例

```typescript
// stores/auth.ts
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { login, getUserInfo, type LoginDto, type User } from "@/api/auth";

export const useAuthStore = defineStore("auth", () => {
  const token = ref<string | null>(localStorage.getItem("token"));
  const user = ref<User | null>(null);
  const menus = ref<any[]>([]);

  const isAuthenticated = computed(() => !!token.value && !!user.value);

  const loginAction = async (loginDto: LoginDto) => {
    const {
      accessToken,
      user: userInfo,
      menus: userMenus,
    } = await login(loginDto);
    token.value = accessToken;
    user.value = userInfo;
    menus.value = userMenus;
    localStorage.setItem("token", accessToken);
  };

  const logout = () => {
    token.value = null;
    user.value = null;
    menus.value = [];
    localStorage.removeItem("token");
  };

  return {
    token,
    user,
    menus,
    isAuthenticated,
    loginAction,
    logout,
  };
});
```

## 路由管理

### 路由规范

- 路由配置在 `router/index.ts`
- 支持动态路由（基于菜单权限）
- 路由路径包含租户编码：`/:tenantCode/xxx`
- 路由 meta 包含权限信息

### 示例

```typescript
{
  path: '/:tenantCode/users',
  name: 'Users',
  component: () => import('@/views/users/Index.vue'),
  meta: {
    requiresAuth: true,
    permissions: ['user:read'],
    roles: ['admin'],
  },
}
```

## 表单验证

### 使用 VeeValidate + Zod

```vue
<script setup lang="ts">
import { useForm } from "vee-validate";
import { toTypedSchema } from "@vee-validate/zod";
import * as z from "zod";

const schema = z.object({
  username: z.string().min(3, "用户名至少3个字符"),
  password: z.string().min(6, "密码至少6个字符"),
  email: z.string().email("邮箱格式不正确").optional(),
});

const { defineField, handleSubmit, errors } = useForm({
  validationSchema: toTypedSchema(schema),
});

const [username] = defineField("username");
const [password] = defineField("password");
const [email] = defineField("email");

const onSubmit = handleSubmit(async (values) => {
  try {
    await createUser(values);
    message.success("创建成功");
  } catch (error) {
    message.error("创建失败");
  }
});
</script>

<template>
  <a-form @submit.prevent="onSubmit">
    <a-form-item
      :help="errors.username"
      :validateStatus="errors.username ? 'error' : ''"
    >
      <a-input v-model:value="username" placeholder="用户名" />
    </a-form-item>
    <a-form-item
      :help="errors.password"
      :validateStatus="errors.password ? 'error' : ''"
    >
      <a-input-password v-model:value="password" placeholder="密码" />
    </a-form-item>
    <a-button type="primary" html-type="submit">提交</a-button>
  </a-form>
</template>
```

## UI 组件规范

### Ant Design Vue

- 使用 Ant Design Vue 组件库
- 遵循 Ant Design 设计规范

### 样式

- 使用 Tailwind CSS 工具类
- 复杂样式使用 SCSS
- 响应式设计，移动端优先

### 状态管理

组件必须有 loading 和 error 状态：

```vue
<template>
  <div class="p-4">
    <a-spin :spinning="loading">
      <a-alert
        v-if="error"
        type="error"
        :message="error"
        closable
        @close="error = null"
      />
      <div v-else>
        <!-- 内容 -->
      </div>
    </a-spin>
  </div>
</template>
```

## TypeScript 类型定义

### 类型文件组织

- TypeScript 类型定义放在 `types/` 目录下
- 接口类型使用 `interface`
- 数据模型使用 `type`
- 导出类型供其他模块使用

### 示例

```typescript
// types/user.ts
export interface User {
  id: number;
  username: string;
  nickname: string;
  email?: string;
  tenantId: number;
  validState: number;
  createTime: string;
  modifyTime: string;
}

export type CreateUserParams = Omit<
  User,
  "id" | "tenantId" | "createTime" | "modifyTime"
>;

export type UserRole = {
  userId: number;
  roleId: number;
  role: Role;
};
```

## 性能优化

### 路由懒加载

```typescript
const routes = [
  {
    path: "/users",
    component: () => import("@/views/users/Index.vue"),
  },
];
```

### 组件按需加载

```typescript
import { defineAsyncComponent } from "vue";

const AsyncComponent = defineAsyncComponent(
  () => import("@/components/HeavyComponent.vue")
);
```

### 避免不必要的重新渲染

使用 `computed`、`watchEffect` 和 `memo` 优化性能。
