# AGENTS.md - haha-admin-web 智能体代码生成规则
> 本文件为 AI Agent 代码生成提供强制规则约束,定义生成边界与规范。
> 所有规则均为强制性,Agent 生成代码时必须严格遵守。违反规则的代码不可提交。
---
## 1. 项目边界
### 1.1 技术栈版本锁定
| 技术 | 版本 | 禁止事项 |
|------|------|----------|
| Node.js | ^20.19.0 \|\| >=22.13.0 | 禁止使用 Node 18 及以下 |
| pnpm | >=9 | 禁止使用 npm/yarn |
| Vue | ^3.5.25 | Composition API only,禁止 Options API |
| TypeScript | ^5.9.3 | 禁止 any 滥用,新代码必须提供类型 |
| Vite | ^7.2.7 | -- |
| Element Plus | ^2.12.0 | 禁止引入 Ant Design / Naive UI 等其他组件库 |
| Pinia | ^3.0.4 | 禁止 Vuex |
| Vue Router | ^4.6.3 | -- |
| Tailwind CSS | ^4.1.17 | 用于工具类,禁止替代组件级样式 |
| ECharts | ^6.0.0 | 图表统一使用 ECharts |
| Axios | ^1.13.2 | HTTP 请求统一使用 `http` 实例,禁止直接 `axios` |
| dayjs | ^1.11.19 | 禁止引入 moment.js |
| Sass | ^1.95.1 | 样式预处理统一使用 SCSS |
### 1.2 后端 API 对接
| 配置项 | 值 |
|--------|-----|
| 后端服务地址 | `http://localhost:7070/admin` |
| 开发端口 | 8888 |
| API 代理 | vite.config.ts 中 `apiPaths` 动态生成 |
| 生产 publicPath | `/admin/` |
| 路由模式 | hash |
### 1.3 目录职责(严格遵循)
```
src/
├── api/ # API 请求函数(每个模块一个文件)
├── assets/ # 静态资源(图标、图片、字体)
├── components/ # 全局公共组件(ReXxx 命名)
├── config/ # 应用配置
├── directives/ # 自定义指令
├── layout/ # 布局框架
├── plugins/ # 插件注册(ElementPlus、I18n、ECharts、VxeTable)
├── router/ # 路由配置(modules/ 下按模块拆分)
├── store/ # Pinia 状态管理(modules/ 下按模块拆分)
├── style/ # 全局样式(reset、index、tailwind、element-plus、dark)
├── utils/ # 工具函数
├── views/ # 页面视图(每个模块一个目录)
│ └── {module}/
│ ├── index.vue # 页面入口
│ └── utils/
│ ├── hook.tsx # 业务逻辑 hook(composable)
│ └── types.ts # 模块类型定义
└── main.ts # 应用入口
```
---
## 2. 代码生成规则
### 2.1 页面视图 (views)
**强制结构**:每个业务页面必须按以下模式组织:
```
views/{module}/
├── index.vue # 纯模板 + 结构,不含业务逻辑
└── utils/
├── hook.tsx # 业务逻辑 composable(export function useXxx)
└── types.ts # 模块内类型定义
```
**index.vue 模板**:
```vue
```
**hook.tsx 模板**:
```tsx
import { ref, reactive, onMounted } from "vue";
import type { PaginationProps } from "@pureadmin/table";
import { message } from "@/utils/message";
import { getXxxList } from "@/api/xxx";
import type { XxxItem, XxxSearchForm } from "./types";
export function useXxx(tableRef: Ref) {
const form = reactive({ /* 初始值 */ });
const dataList = ref([]);
const loading = ref(true);
const pagination = reactive({
total: 0,
pageSize: 10,
currentPage: 1,
background: true
});
const columns: TableColumnList = [
{ label: "xxx", prop: "xxx", minWidth: 120 },
// ...
];
const onSearch = async () => {
loading.value = true;
try {
const { data } = await getXxxList({ ...form, page: pagination.currentPage, pageSize: pagination.pageSize });
dataList.value = data.list;
pagination.total = data.total;
} finally {
loading.value = false;
}
};
const resetForm = (formEl: any) => {
if (!formEl) return;
formEl.resetFields();
onSearch();
};
const handleSizeChange = (val: number) => {
pagination.pageSize = val;
onSearch();
};
const handleCurrentChange = (val: number) => {
pagination.currentPage = val;
onSearch();
};
onMounted(() => { onSearch(); });
return {
form, loading, columns, dataList, pagination,
onSearch, resetForm, handleSizeChange, handleCurrentChange
};
}
```
**types.ts 模板**:
```ts
export interface XxxItem {
id: number;
// 字段定义
}
export interface XxxSearchForm {
// 搜索表单字段
}
```
### 2.2 API 层 (api/)
**强制规则**:
1. 每个 API 文件对应一个后端模块,文件名与后端路径一致
2. 统一使用 `http.request(method, url, config)` 调用
3. 必须定义 `Result` 和 `ResultTable` 类型(可复用已有定义)
**API 文件模板**:
```ts
import { http } from "@/utils/http";
type Result = {
code: number;
message: string;
data?: any;
};
type ResultTable = {
code: number;
message: string;
data?: {
list: Array;
total: number;
pageSize: number;
currentPage: number;
};
};
// 列表查询
export const getXxxList = (params: {
page?: number;
pageSize?: number;
// ...搜索参数
}) => {
return http.request("get", "/xxx/list", { params });
};
// 详情查询
export const getXxxById = (id: number) => {
return http.request("get", `/xxx/${id}`);
};
// 新增
export const createXxx = (data: any) => {
return http.request("post", "/xxx", { data });
};
// 修改状态
export const updateXxxStatus = (id: number, status: number) => {
return http.request("put", `/xxx/${id}/status?status=${status}`);
};
// 删除
export const deleteXxx = (id: number) => {
return http.request("delete", `/xxx/${id}`);
};
```
**禁止事项**:
- 禁止直接 `import axios` 调用,必须使用 `http` 实例
- 禁止在 API 函数中处理 UI 逻辑(如 ElMessage)
- 禁止硬编码后端地址
### 2.3 路由 (router/modules/)
**路由文件模板**:
```ts
export default {
path: "/xxx",
redirect: "/xxx/list",
meta: {
icon: "ri:xxx-line", // Remix Icon
title: "xxx管理",
rank: 数字 // 菜单排序
},
children: [
{
path: "/xxx/list",
name: "XxxList", // PascalCase
component: () => import("@/views/xxx/index.vue"),
meta: {
icon: "ri:xxx-line",
title: "xxx管理"
}
}
]
} satisfies RouteConfigsTable;
```
**强制规则**:
- 路由 path 使用 kebab-case
- 路由 name 使用 PascalCase
- 图标统一使用 Remix Icon(`ri:xxx-line`)
- 懒加载必须用 `() => import()` 语法
### 2.4 Store (store/modules/)
**Store 模板**:
```ts
import { defineStore } from "pinia";
import { store } from "../utils";
export const useXxxStore = defineStore("pure-xxx", {
state: (): XxxState => ({
// 状态
}),
actions: {
// 方法命名:SET_XXX (修改状态)、async方法 (异步操作)
}
});
export function useXxxStoreHook() {
return useXxxStore(store);
}
```
**强制规则**:
- Store ID 前缀 `pure-`
- 必须导出 `useXxxStoreHook` 辅助函数
- 状态修改方法以 `SET_` 前缀命名
### 2.5 组件 (components/)
**强制规则**:
- 全局公共组件以 `Re` 前缀命名(如 `ReDialog`、`ReIcon`)
- 组件导出使用 `index.ts` 桶文件
- 禁止在公共组件中写入业务逻辑
---
## 3. 样式规则
### 3.1 样式方案
| 场景 | 方案 | 说明 |
|------|------|------|
| 布局与间距 | Tailwind CSS | `w-full`、`pl-8`、`pt-[12px]` 等 |
| 组件级样式 | `