本文件为 AI Agent 代码生成提供强制规则约束,定义生成边界与规范。 所有规则均为强制性,Agent 生成代码时必须严格遵守。违反规则的代码不可提交。
| 技术 | 版本 | 禁止事项 |
|---|---|---|
| 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 |
| 配置项 | 值 |
|---|---|
| 后端服务地址 | http://localhost:7070/admin |
| 开发端口 | 8888 |
| API 代理 | vite.config.ts 中 apiPaths 动态生成 |
| 生产 publicPath | /admin/ |
| 路由模式 | hash |
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 # 应用入口
强制结构:每个业务页面必须按以下模式组织:
views/{module}/
├── index.vue # 纯模板 + 结构,不含业务逻辑
└── utils/
├── hook.tsx # 业务逻辑 composable(export function useXxx)
└── types.ts # 模块内类型定义
index.vue 模板:
<script setup lang="ts">
import { ref } from "vue";
import { useXxx } from "./utils/hook"; // 使用 hook.tsx 中的 composable
import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
defineOptions({
name: "XxxManage" // 必须:组件名使用 PascalCase + Manage
});
const formRef = ref();
const tableRef = ref();
const {
form, loading, columns, dataList, pagination,
onSearch, resetForm, handleSizeChange, handleCurrentChange,
// ...其他业务方法
} = useXxx(tableRef);
</script>
<template>
<div class="main">
<!-- 搜索表单 -->
<el-form ref="formRef" :inline="true" :model="form"
class="search-form bg-bg_color w-full pl-8 pt-[12px] overflow-auto">
<!-- el-form-item 列表 -->
</el-form>
<!-- 表格 -->
<PureTableBar title="xxx管理" :columns="columns" @refresh="onSearch">
<template v-slot="{ size, dynamicColumns }">
<pure-table
ref="tableRef"
row-key="id"
adaptive
:adaptiveConfig="{ offsetBottom: 108 }"
align-whole="center"
table-layout="auto"
:loading="loading"
:size="size"
:data="dataList"
:columns="dynamicColumns"
:pagination="{ ...pagination, size }"
:header-cell-style="{
background: 'var(--el-fill-color-light)',
color: 'var(--el-text-color-primary)'
}"
@page-size-change="handleSizeChange"
@page-current-change="handleCurrentChange"
>
<template #operation="{ row }">
<!-- 操作按钮 -->
</template>
</pure-table>
</template>
</PureTableBar>
</div>
</template>
<style lang="scss" scoped>
.search-form {
:deep(.el-form-item) {
margin-bottom: 12px;
}
}
</style>
hook.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<XxxSearchForm>({ /* 初始值 */ });
const dataList = ref([]);
const loading = ref(true);
const pagination = reactive<PaginationProps>({
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 模板:
export interface XxxItem {
id: number;
// 字段定义
}
export interface XxxSearchForm {
// 搜索表单字段
}
强制规则:
http.request<T>(method, url, config) 调用Result 和 ResultTable 类型(可复用已有定义)API 文件模板:
import { http } from "@/utils/http";
type Result = {
code: number;
message: string;
data?: any;
};
type ResultTable = {
code: number;
message: string;
data?: {
list: Array<any>;
total: number;
pageSize: number;
currentPage: number;
};
};
// 列表查询
export const getXxxList = (params: {
page?: number;
pageSize?: number;
// ...搜索参数
}) => {
return http.request<ResultTable>("get", "/xxx/list", { params });
};
// 详情查询
export const getXxxById = (id: number) => {
return http.request<Result>("get", `/xxx/${id}`);
};
// 新增
export const createXxx = (data: any) => {
return http.request<Result>("post", "/xxx", { data });
};
// 修改状态
export const updateXxxStatus = (id: number, status: number) => {
return http.request<Result>("put", `/xxx/${id}/status?status=${status}`);
};
// 删除
export const deleteXxx = (id: number) => {
return http.request<Result>("delete", `/xxx/${id}`);
};
禁止事项:
import axios 调用,必须使用 http 实例路由文件模板:
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;
强制规则:
ri:xxx-line)() => import() 语法Store 模板:
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);
}
强制规则:
pure-useXxxStoreHook 辅助函数SET_ 前缀命名强制规则:
Re 前缀命名(如 ReDialog、ReIcon)index.ts 桶文件| 场景 | 方案 | 说明 |
|---|---|---|
| 布局与间距 | Tailwind CSS | w-full、pl-8、pt-[12px] 等 |
| 组件级样式 | <style lang="scss" scoped> |
必须加 scoped |
| 全局样式 | src/style/ |
reset、主题、动画 |
| 深度选择器 | :deep() |
禁止 ::v-deep、/deep/、>>> |
style="" 属性(Tailwind 例外)authorized-token)+ localStorage(user-info)Authorization: Bearer {token}PureHttp 类内置无感刷新机制| 层级 | 实现方式 | 示例 |
|---|---|---|
| 路由级别 | meta.roles |
路由守卫校验 |
| 按钮级别 | <Auth> 组件 / v-perms 指令 |
<Auth value="device:open">按钮</Auth> |
| API 级别 | 后端 @RequirePermission |
后端控制 |
login() APIsetToken() 存储 token 到 Cookie + localStorageuseUserStoreHook().SET_ROLES/SET_PERMS 存储权限initRouter() 动态加载菜单后端统一返回格式:
type Result = {
code: number; // 200=成功
message: string;
data?: any;
};
type ResultTable = {
code: number;
message: string;
data?: {
list: Array<any>;
total: number;
pageSize: number;
currentPage: number;
};
};
src/utils/http/ 中的 http 实例application/jsonimport axios| 类型 | 命名风格 | 示例 |
|---|---|---|
| 文件名 | kebab-case | device-manage.vue、order.ts |
| 组件名 | PascalCase | DeviceManage、PureTableBar |
| 变量/函数 | camelCase | handleOpenDoor、dataList |
| 常量 | UPPER_SNAKE_CASE | VITE_PORT |
| CSS 类名 | kebab-case / BEM | search-form、order-item |
| 路由 path | kebab-case | /device/list |
| Store ID | kebab-case + pure- 前缀 |
pure-user |
| API 函数 | camelCase + 动词前缀 | getDeviceList、createCoupon |
@commitlint/config-conventionalfeat、fix、docs、style、refactor、test、choredebugger(已配置 no-debugger: off 但不推荐)| 编号 | 陷阱 | 规则 |
|---|---|---|
| T1 | 直接使用 axios |
禁止。必须使用 http 实例 |
| T2 | 在 API 层处理 UI 逻辑 | 禁止。API 层只负责请求,UI 反馈在 hook 或组件中处理 |
| T3 | 组件内写非 scoped 样式 | 禁止。必须 <style lang="scss" scoped> |
| T4 | 不提供 TypeScript 类型 | 禁止。新代码必须提供类型定义 |
| T5 | 在 index.vue 中写业务逻辑 | 禁止。逻辑必须抽离到 utils/hook.tsx |
| T6 | 路由不使用懒加载 | 禁止。必须 () => import() |
| T7 | 使用 Vuex | 禁止。统一使用 Pinia |
| T8 | 使用 moment.js | 禁止。统一使用 dayjs |
| T9 | 修改全局样式时不走 style/ | 禁止。全局样式改动必须修改 src/style/ 下文件 |
| T10 | 分页不使用 PaginationProps |
禁止。分页配置必须使用 @pureadmin/table 的 PaginationProps |
| T11 | 表格不使用 PureTableBar |
禁止。列表页必须使用 PureTableBar 包裹 |
| T12 | 图标不使用 Remix Icon | 禁止。统一使用 ri: 前缀图标或 ~icons/ 前缀自动导入 |
| T13 | 表单重置不调用 resetFields |
禁止。重置必须使用 Element Plus 的 resetFields() |