2026-03-27 22:20:25 +08:00
|
|
|
|
---
|
|
|
|
|
|
description: 前端特定的开发规范(仅作用于 frontend 目录)
|
|
|
|
|
|
globs:
|
|
|
|
|
|
alwaysApply: true
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# 前端特定规范
|
|
|
|
|
|
|
|
|
|
|
|
本规则仅作用于 `frontend/` 目录。
|
|
|
|
|
|
|
|
|
|
|
|
## Ant Design Vue 组件使用
|
|
|
|
|
|
|
|
|
|
|
|
### 表格组件
|
|
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<a-table
|
|
|
|
|
|
:columns="columns"
|
|
|
|
|
|
:data-source="dataSource"
|
|
|
|
|
|
:loading="loading"
|
|
|
|
|
|
:pagination="pagination"
|
|
|
|
|
|
@change="handleTableChange"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
|
<template v-if="column.key === 'action'">
|
|
|
|
|
|
<a-space>
|
|
|
|
|
|
<a-button type="link" @click="handleEdit(record)">编辑</a-button>
|
|
|
|
|
|
<a-popconfirm
|
|
|
|
|
|
title="确定要删除吗?"
|
|
|
|
|
|
@confirm="handleDelete(record.id)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-button type="link" danger>删除</a-button>
|
|
|
|
|
|
</a-popconfirm>
|
|
|
|
|
|
</a-space>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 表单组件
|
|
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<a-form
|
|
|
|
|
|
:model="formState"
|
|
|
|
|
|
:rules="rules"
|
|
|
|
|
|
layout="vertical"
|
|
|
|
|
|
@finish="onFinish"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-form-item label="用户名" name="username">
|
|
|
|
|
|
<a-input v-model:value="formState.username" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<a-form-item>
|
|
|
|
|
|
<a-button type="primary" html-type="submit">提交</a-button>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</a-form>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Modal 弹窗
|
|
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<a-modal
|
|
|
|
|
|
v-model:open="visible"
|
|
|
|
|
|
title="编辑用户"
|
|
|
|
|
|
@ok="handleOk"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-form :model="formState">
|
|
|
|
|
|
<!-- 表单内容 -->
|
|
|
|
|
|
</a-form>
|
|
|
|
|
|
</a-modal>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 消息提示
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
import { message, notification } from 'ant-design-vue';
|
|
|
|
|
|
|
|
|
|
|
|
// 成功提示
|
|
|
|
|
|
message.success('操作成功');
|
|
|
|
|
|
|
|
|
|
|
|
// 错误提示
|
|
|
|
|
|
message.error('操作失败');
|
|
|
|
|
|
|
|
|
|
|
|
// 通知
|
|
|
|
|
|
notification.success({
|
|
|
|
|
|
message: '成功',
|
|
|
|
|
|
description: '用户创建成功',
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Tailwind CSS 常用类
|
|
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
|
<!-- 布局 -->
|
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
|
<div class="grid grid-cols-3 gap-4">
|
|
|
|
|
|
<div class="p-4 m-2">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 响应式 -->
|
|
|
|
|
|
<div class="w-full md:w-1/2 lg:w-1/3">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 文本 -->
|
|
|
|
|
|
<p class="text-lg font-bold text-gray-800">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 状态 -->
|
|
|
|
|
|
<button class="hover:bg-blue-600 active:bg-blue-700">
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 权限指令
|
|
|
|
|
|
|
|
|
|
|
|
使用自定义指令 `v-permission`:
|
|
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<a-button v-permission="'user:create'" type="primary">
|
|
|
|
|
|
创建用户
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
|
|
|
|
|
|
<a-button v-permission="['user:update', 'user:delete']" type="link">
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</a-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 租户路由
|
|
|
|
|
|
|
|
|
|
|
|
所有路由必须包含租户编码:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// ✅ 正确
|
|
|
|
|
|
router.push(`/${tenantCode}/users`);
|
|
|
|
|
|
|
|
|
|
|
|
// ❌ 错误
|
|
|
|
|
|
router.push('/users');
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
获取租户编码:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
import { useRoute } from 'vue-router';
|
|
|
|
|
|
|
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
|
const tenantCode = route.params.tenantCode as string;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 常用组合式函数
|
|
|
|
|
|
|
|
|
|
|
|
### 使用权限检查
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
import { useAuthStore } from '@/stores/auth';
|
|
|
|
|
|
|
|
|
|
|
|
const authStore = useAuthStore();
|
|
|
|
|
|
|
|
|
|
|
|
// 检查权限
|
|
|
|
|
|
const hasPermission = authStore.hasPermission('user:create');
|
|
|
|
|
|
|
|
|
|
|
|
// 检查多个权限(任一)
|
|
|
|
|
|
const hasAnyPermission = authStore.hasAnyPermission(['user:create', 'user:update']);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 表格分页
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
import { ref, reactive } from 'vue';
|
|
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
const dataSource = ref([]);
|
|
|
|
|
|
const pagination = reactive({
|
|
|
|
|
|
current: 1,
|
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
total: 0,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const handleTableChange = (pag: any) => {
|
|
|
|
|
|
pagination.current = pag.current;
|
|
|
|
|
|
pagination.pageSize = pag.pageSize;
|
|
|
|
|
|
fetchData();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const fetchData = async () => {
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { data, total } = await getUsers({
|
|
|
|
|
|
skip: (pagination.current - 1) * pagination.pageSize,
|
|
|
|
|
|
take: pagination.pageSize,
|
|
|
|
|
|
});
|
|
|
|
|
|
dataSource.value = data;
|
|
|
|
|
|
pagination.total = total;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 项目结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/
|
|
|
|
|
|
├── api/ # API 接口
|
|
|
|
|
|
├── assets/ # 静态资源
|
|
|
|
|
|
├── components/ # 公共组件
|
|
|
|
|
|
├── composables/ # 组合式函数
|
|
|
|
|
|
├── directives/ # 自定义指令
|
|
|
|
|
|
├── layouts/ # 布局组件
|
|
|
|
|
|
├── router/ # 路由配置
|
|
|
|
|
|
├── stores/ # Pinia 状态
|
|
|
|
|
|
├── styles/ # 全局样式
|
|
|
|
|
|
├── types/ # TypeScript 类型
|
|
|
|
|
|
├── utils/ # 工具函数
|
|
|
|
|
|
└── views/ # 页面组件
|
|
|
|
|
|
├── auth/ # 认证相关
|
|
|
|
|
|
├── users/ # 用户管理
|
|
|
|
|
|
├── school/ # 学校管理
|
|
|
|
|
|
└── contests/ # 竞赛管理
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 常用脚本
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 开发
|
|
|
|
|
|
pnpm dev
|
|
|
|
|
|
|
|
|
|
|
|
# 构建
|
|
|
|
|
|
pnpm build
|
|
|
|
|
|
|
|
|
|
|
|
# 预览
|
|
|
|
|
|
pnpm preview
|
|
|
|
|
|
|
|
|
|
|
|
# 代码检查
|
|
|
|
|
|
pnpm lint
|
|
|
|
|
|
```
|