📝 主要改进: • Cursor配置模块化:拆分为前端、后端、移动端、文档、API测试5个专门规范文件 • API文档重新组织:按业务模块拆分到docs/开发文档/API/目录下 • 新增维修分类功能完整测试方案和自动化测试脚本 • 后端热启动脚本完善,支持Linux/Windows双平台 • 强化文档同步更新规范,确保代码与文档一致性 🔧 技术改进: • 优化Cursor AI工作流程和代码生成质量 • 完善API测试规范(AES+RSA双重加密登录方法) • 提升开发效率和代码质量标准化程度 �� 测试覆盖: • 维修分类管理功能端到端测试验证通过 • 包含页面加载、CRUD操作、搜索功能等核心测试用例 更新时间:2025-07-01
18 KiB
18 KiB
Cursor Rules - Web 前端开发规范
适用范围
- 项目目录:
web/(基于 Vue 3 + TypeScript + Element Plus) - 开发环境: Node.js 20.15.x+, npm 10.7.x+
- 前端访问: http://localhost (默认 80 端口)
Web 前端开发规范 (web/)
1. 技术栈规范
核心技术
- 框架: Vue 3.5.13 + TypeScript 5.8.3
- UI 组件库: Element Plus 2.9.8
- 构建工具: Vite 7.0.9
- 状态管理: Pinia
- 路由管理: Vue Router 4
- HTTP 客户端: Axios
- 代码风格: ESLint + Prettier
- Node.js: 20.15.x+ (推荐 20.15.0)
- npm: 10.7.x+
环境要求
- 开发环境: 支持本地环境或阿里云测试环境
- 数据库: MySQL/Redis 支持本机安装或使用阿里云环境
- 前端访问: http://localhost (默认 80 端口)
- 后端服务: http://localhost:8080
2. Art Design Pro UI 规范
设计理念
基于 Art Design Pro 的设计语言,追求现代化、精致、用户友好的界面体验。注重细节、动效和用户交互的流畅性。
颜色系统
主色调 (Primary Colors)
--art-primary: 93, 135, 255; /* 主品牌色 - 蓝色 */
--art-secondary: 73, 190, 255; /* 辅助色 - 天蓝色 */
--art-success: 19, 222, 185; /* 成功色 - 绿色 */
--art-warning: 255, 174, 31; /* 警告色 - 橙色 */
--art-error: 250, 137, 107; /* 错误色 - 红色 */
--art-info: 83, 155, 255; /* 信息色 - 蓝色 */
--art-danger: 255, 77, 79; /* 危险色 - 红色 */
背景色系统
/* 浅色主题 */
--art-bg-color: #fafbfc; /* 页面底色 */
--art-main-bg-color: #ffffff; /* 主要内容背景 */
--art-bg-primary: 236, 242, 255; /* 主色背景 */
--art-bg-secondary: 232, 247, 255; /* 辅助背景 */
--art-bg-success: 230, 255, 250; /* 成功背景 */
--art-bg-warning: 254, 245, 229; /* 警告背景 */
--art-bg-error: 253, 237, 232; /* 错误背景 */
/* 暗色主题 */
html.dark {
--art-bg-color: #070707; /* 页面底色 */
--art-main-bg-color: #161618; /* 主要内容背景 */
}
轮廓式按钮设计系统
🎨 设计理念
全面采用轮廓式(Outline)按钮设计,提供更加优雅、现代和一致的用户界面体验。
核心规范
- Primary 按钮: 轮廓式主色设计,主色边框和文字,5%透明度主色背景
- Success 按钮: 轮廓式成功色设计,成功色边框和文字,5%透明度成功色背景
- Warning 按钮: 轮廓式警告色设计,警告色边框和文字,5%透明度警告色背景
- Danger 按钮: 轮廓式危险色设计,危险色边框和文字,5%透明度危险色背景
- Info 按钮: 轮廓式信息色设计,信息色边框和文字,5%透明度信息色背景
- Default 按钮: 白色背景,灰色边框,灰色文字
交互效果
- 悬浮状态: 上移 1px + 背景透明度变为 10% + 边框透明度变为 80% + 彩色阴影
- 点击状态: 98%缩放 + 0.1s 快速过渡 + 背景透明度变为 15%
- 禁用状态: 50%透明度,禁用所有交互和变换
3. 组件开发规范
Vue 组件结构标准
<template>
<div class="component-container">
<!-- 组件内容 -->
</div>
</template>
<script setup lang="ts" name="ComponentName">
// 1. 引入依赖
import { ref, reactive, computed, onMounted } from 'vue';
import type { ComponentProps, ComponentEmits } from './types';
// 2. 定义Props和Emits
const props = defineProps<ComponentProps>();
const emits = defineEmits<ComponentEmits>();
// 3. 响应式数据
const loading = ref(false);
const formData = reactive({});
// 4. 计算属性
const computedValue = computed(() => {
return /* 计算逻辑 */;
});
// 5. 方法定义
const handleClick = () => {
// 方法实现
};
// 6. 生命周期
onMounted(() => {
// 组件挂载后的逻辑
});
</script>
<style lang="scss" scoped>
.component-container {
// 样式实现
}
</style>
页面开发规范
列表页面标准模板
<template>
<div class="p-2 page-container">
<!-- 搜索区域 -->
<transition
:enter-active-class="proxy?.animate.searchAnimate.enter"
:leave-active-class="proxy?.animate.searchAnimate.leave"
>
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form
ref="queryFormRef"
:model="queryParams"
:inline="true"
label-width="100px"
>
<!-- 搜索表单项 -->
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">
搜索
</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<!-- 表格区域 -->
<el-card shadow="never" class="button-card table-card">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd">
新增
</el-button>
</el-col>
<right-toolbar
v-model:showSearch="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
</template>
<!-- 数据表格 -->
<el-table v-loading="loading" border :data="dataList" :height="tableHeight">
<el-table-column type="selection" width="55" align="center" />
<!-- 数据列 -->
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<!-- 操作按钮 -->
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</el-card>
</div>
</template>
表单页面标准模板
<template>
<div class="app-container">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-card shadow="never">
<template #header>
<span>基本信息</span>
</template>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态">
<el-option label="启用" value="0" />
<el-option label="禁用" value="1" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-form-item>
</el-card>
</el-form>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
// 表单引用
const formRef = ref<FormInstance>();
// 表单数据
const form = reactive({
name: '',
status: '0',
});
// 验证规则
const rules = reactive<FormRules>({
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
});
// 提交表单
const submitForm = async () => {
if (!formRef.value) return;
await formRef.value.validate((valid) => {
if (valid) {
// 提交逻辑
console.log('提交表单:', form);
}
});
};
// 重置表单
const resetForm = () => {
if (!formRef.value) return;
formRef.value.resetFields();
};
</script>
4. API 开发规范
API 接口定义标准
// api/[module]/index.ts
import request from '@/utils/request';
import type { AxiosPromise } from 'axios';
import type { DataVO, DataForm, DataQuery } from './types';
/**
* 查询数据列表
* @param query 查询参数
*/
export const listData = (query?: DataQuery): AxiosPromise<DataVO[]> => {
return request({
url: '/api/data/list',
method: 'get',
params: query,
});
};
/**
* 查询数据详情
* @param id 数据ID
*/
export const getData = (id: string | number): AxiosPromise<DataVO> => {
return request({
url: `/api/data/${id}`,
method: 'get',
});
};
/**
* 新增数据
* @param data 数据信息
*/
export const addData = (data: DataForm): AxiosPromise<void> => {
return request({
url: '/api/data',
method: 'post',
data: data,
});
};
/**
* 修改数据
* @param data 数据信息
*/
export const updateData = (data: DataForm): AxiosPromise<void> => {
return request({
url: '/api/data',
method: 'put',
data: data,
});
};
/**
* 删除数据
* @param ids 数据ID数组
*/
export const delData = (ids: Array<string | number>): AxiosPromise<void> => {
return request({
url: `/api/data/${ids}`,
method: 'delete',
});
};
TypeScript 类型定义
// api/[module]/types.ts
// 查询参数
export interface DataQuery extends PageQuery {
name?: string;
status?: string;
createTime?: [DateModelType, DateModelType];
}
// 数据表单
export interface DataForm {
id?: string | number;
name: string;
status: string;
remark?: string;
}
// 数据视图对象
export interface DataVO {
id: string | number;
name: string;
status: string;
createTime: string;
updateTime: string;
remark?: string;
}
5. 状态管理规范
Pinia Store 结构
// stores/modules/[module].ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import type { DataVO, DataQuery } from '@/api/[module]/types';
import { listData, getData } from '@/api/[module]';
export const useDataStore = defineStore('[module]', () => {
// 状态
const dataList = ref<DataVO[]>([]);
const loading = ref(false);
const total = ref(0);
// 计算属性
const hasData = computed(() => dataList.value.length > 0);
// 方法
const getList = async (query: DataQuery) => {
loading.value = true;
try {
const { data } = await listData(query);
dataList.value = data.rows;
total.value = data.total;
} finally {
loading.value = false;
}
};
const getInfo = async (id: string | number) => {
const { data } = await getData(id);
return data;
};
const reset = () => {
dataList.value = [];
total.value = 0;
};
return {
// 状态
dataList,
loading,
total,
// 计算属性
hasData,
// 方法
getList,
getInfo,
reset,
};
});
6. 路由管理规范
路由定义标准
// router/modules/[module].ts
import type { RouteRecordRaw } from 'vue-router';
import Layout from '@/layout/index.vue';
const moduleRouter: RouteRecordRaw = {
path: '/[module]',
component: Layout,
redirect: '/[module]/list',
name: 'Module',
meta: {
title: '模块管理',
icon: 'example',
alwaysShow: true,
},
children: [
{
path: 'list',
component: () => import('@/views/[module]/index.vue'),
name: 'ModuleList',
meta: {
title: '列表管理',
activeMenu: '/[module]/list',
},
},
{
path: 'add',
component: () => import('@/views/[module]/form.vue'),
name: 'ModuleAdd',
meta: {
title: '新增数据',
activeMenu: '/[module]/list',
},
hidden: true,
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/[module]/form.vue'),
name: 'ModuleEdit',
meta: {
title: '编辑数据',
activeMenu: '/[module]/list',
},
hidden: true,
},
],
};
export default moduleRouter;
7. 项目架构规范
目录结构标准
web/src/
├── api/ # API接口定义
│ ├── [module]/ # 按模块分类
│ │ ├── index.ts # API接口
│ │ └── types.ts # 类型定义
│ └── types.ts # 通用类型定义
├── assets/ # 静态资源
│ ├── icons/ # SVG图标
│ ├── images/ # 图片资源
│ └── styles/ # 全局样式
├── components/ # 通用组件
│ ├── [ComponentName]/ # 组件目录
│ │ ├── index.vue # 组件主文件
│ │ └── types.ts # 组件类型
│ └── index.ts # 组件导出
├── composables/ # 组合式函数
├── directive/ # 自定义指令
├── hooks/ # 自定义钩子
├── layout/ # 布局组件
├── plugins/ # 插件配置
├── router/ # 路由配置
│ ├── modules/ # 路由模块
│ └── index.ts # 路由入口
├── stores/ # 状态管理
│ ├── modules/ # Store模块
│ └── index.ts # Store入口
├── types/ # 全局类型定义
├── utils/ # 工具函数
│ ├── request.ts # HTTP请求封装
│ ├── auth.ts # 认证工具
│ └── index.ts # 工具导出
├── views/ # 页面视图
│ ├── [module]/ # 按模块分类
│ │ ├── index.vue # 列表页面
│ │ ├── form.vue # 表单页面
│ │ └── components/ # 页面组件
│ └── layout/ # 布局页面
├── App.vue # 应用入口
└── main.ts # 主入口文件
8. 代码规范
命名规范
- 文件命名: 使用 kebab-case (如: user-list.vue)
- 组件命名: 使用 PascalCase (如: UserList)
- 方法命名: 使用 camelCase (如: getUserList)
- 常量命名: 使用 UPPER_SNAKE_CASE (如: API_BASE_URL)
- CSS 类名: 使用 kebab-case (如: .user-list-container)
ESLint 配置
// .eslintrc.js
module.exports = {
extends: ['@vue/eslint-config-typescript', '@vue/eslint-config-prettier'],
rules: {
// Vue规则
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
// TypeScript规则
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-explicit-any': 'warn',
// 通用规则
'no-console': 'warn',
'no-debugger': 'error',
},
};
Prettier 配置
{
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"tabWidth": 2,
"useTabs": false,
"printWidth": 100,
"endOfLine": "lf"
}
9. 性能优化
代码分割
// 路由懒加载
const Home = () => import('@/views/home/index.vue');
// 组件懒加载
const LazyComponent = defineAsyncComponent(() => import('@/components/LazyComponent.vue'));
缓存策略
// HTTP缓存
import { ElMessage } from 'element-plus';
const request = axios.create({
timeout: 10000,
headers: {
'Cache-Control': 'no-cache'
}
});
// 组件缓存
<router-view v-slot="{ Component }">
<keep-alive :include="cachedViews">
<component :is="Component" :key="route.path" />
</keep-alive>
</router-view>
10. 错误处理
全局错误处理
// main.ts
app.config.errorHandler = (err, vm, info) => {
console.error('全局错误:', err);
ElMessage.error('系统异常,请联系管理员');
};
// 请求错误处理
request.interceptors.response.use(
(response) => {
const { code, msg } = response.data;
if (code === 200) {
return response.data;
} else {
ElMessage.error(msg || '请求失败');
return Promise.reject(new Error(msg));
}
},
(error) => {
ElMessage.error('网络异常');
return Promise.reject(error);
},
);
11. 测试规范
单元测试
// tests/components/UserList.spec.ts
import { mount } from '@vue/test-utils';
import UserList from '@/components/UserList/index.vue';
describe('UserList', () => {
it('should render correctly', () => {
const wrapper = mount(UserList, {
props: {
users: [],
},
});
expect(wrapper.exists()).toBe(true);
});
});
E2E 测试
// e2e/user.spec.ts
import { test, expect } from '@playwright/test';
test('用户列表页面', async ({ page }) => {
await page.goto('/user/list');
await expect(page.locator('.user-list')).toBeVisible();
});