Files
hzdyjs/.cursor/cursorrules-frontend.md
T
zlwu 249b1dfeb4 feat: 完善项目开发规范和测试文档体系
📝 主要改进:
• Cursor配置模块化:拆分为前端、后端、移动端、文档、API测试5个专门规范文件
• API文档重新组织:按业务模块拆分到docs/开发文档/API/目录下
• 新增维修分类功能完整测试方案和自动化测试脚本
• 后端热启动脚本完善,支持Linux/Windows双平台
• 强化文档同步更新规范,确保代码与文档一致性

🔧 技术改进:
• 优化Cursor AI工作流程和代码生成质量
• 完善API测试规范(AES+RSA双重加密登录方法)
• 提升开发效率和代码质量标准化程度

�� 测试覆盖:
• 维修分类管理功能端到端测试验证通过
• 包含页面加载、CRUD操作、搜索功能等核心测试用例

更新时间:2025-07-01
2025-07-01 17:53:05 +08:00

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();
});