🚀 前端代码生成标准模板与代码规范约束规则

基于 OpenClaw + Claude Code 的端到端研发自动化系统 · 前端研发岗位 Agent 专用

📋 文档版本:v1.0.0 📅 更新日期:2026-03-13 RELEASE

📑 目录导航

1. 概述与目标

🎯 核心目标:建立统一的前端代码生成标准,确保 AI 生成的代码符合企业级开发规范,支持从需求到部署的全流程自动化。

1.1 适用范围

🔧 技术栈支持
  • React 18+ / Vue 3+
  • TypeScript 5+
  • Next.js 14+ / Nuxt 3+
  • TailwindCSS / SCSS / CSS Modules
  • Zustand / Redux Toolkit / Pinia
  • Vitest / Jest / React Testing Library
  • Playwright / Cypress
👥 适用角色
  • 前端研发 Agent(AI 代码生成)
  • 人机协同开发场景
  • 代码审查自动化系统
  • CI/CD 流水线质量门禁
  • 技术债务检测工具

1.2 规范层级

层级 约束类型 执行方式 违规处理
L1 - 基础规范 强制 ESLint + Prettier 构建失败
L2 - 架构规范 强制 架构检查工具 Code Review 拦截
L3 - 最佳实践 推荐 AI 提示词约束 警告提示
L4 - 性能规范 推荐 Lighthouse CI 性能报告

2. 命名规范约束规则

2.1 文件命名规范

文件类型 命名规则 示例 优先级
React 组件 PascalCase.tsx UserProfile.tsx, OrderList.tsx 强制
Vue 组件 PascalCase.vue UserProfile.vue, OrderList.vue 强制
工具函数 camelCase.ts formatDate.ts, validateEmail.ts 强制
常量文件 UPPER_CASE.ts API_ENDPOINTS.ts, ROUTES.ts 强制
类型定义 PascalCase.types.ts User.types.ts, Api.types.ts 强制
测试文件 *.test.ts(x) UserProfile.test.tsx 强制
样式文件 *.module.css/scss UserProfile.module.css 推荐

2.2 变量与函数命名

// ✅ 正确示例
const userData = { // camelCase 用于变量
    userName: 'John',
    userAge: 25
};

function calculateTotalPrice() { // camelCase 用于函数
    return price * quantity;
}

const MAX_RETRY_COUNT = 3; // UPPER_CASE 用于常量

interface UserProfile { // PascalCase 用于类型
    id: string;
    name: string;
}

// ❌ 错误示例 - AI 生成时必须避免
const user_data = {}; // 不使用 snake_case
function CalculateTotal() {} // 函数不使用 PascalCase
const maxRetryCount = 3; // 常量必须 UPPER_CASE

2.3 组件命名约束

// ✅ 组件命名规则
// 1. 组件名必须与文件名一致
// 2. 使用业务语义命名,避免通用名
// 3. 列表组件使用复数形式

// 好示例
export function UserProfile() { // 具体业务语义
    return <div>...</div>;
}

export function OrderList() { // 列表使用复数
    return <ul>...</ul>;
}

// 坏示例 - AI 不应生成
export function Component1() {} // 无意义命名
export function Page() {} // 过于通用
export function Order() {} // 列表应该用复数

3. 项目结构规范

3.1 标准项目目录结构

# React + TypeScript 项目结构模板
project-root/
├── src/
│   ├── components/           # 可复用组件
│   │   ├── ui/              # 基础 UI 组件
│   │   │   ├── Button/
│   │   │   │   ├── Button.tsx
│   │   │   │   ├── Button.module.css
│   │   │   │   ├── Button.types.ts
│   │   │   │   └── Button.test.tsx
│   │   │   └── Input/
│   │   ├── business/        # 业务组件
│   │   │   ├── UserProfile/
│   │   │   └── OrderList/
│   │   └── layouts/         # 布局组件
│   ├── pages/               # 页面组件 (Next.js: app/)
│   │   ├── Home/
│   │   ├── User/
│   │   └── Order/
│   ├── hooks/               # 自定义 Hooks
│   │   ├── useAuth.ts
│   │   ├── useFetch.ts
│   │   └── useLocalStorage.ts
│   ├── services/            # API 服务层
│   │   ├── api.ts
│   │   ├── userService.ts
│   │   ├── orderService.ts
│   │   └── interceptors.ts
│   ├── store/               # 状态管理
│   │   ├── slices/
│   │   ├── store.ts
│   │   └── selectors.ts
│   ├── types/               # 全局类型定义
│   │   ├── api.types.ts
│   │   ├── user.types.ts
│   │   └── common.types.ts
│   ├── utils/               # 工具函数
│   │   ├── format.ts
│   │   ├── validate.ts
│   │   └── helpers.ts
│   ├── constants/           # 常量定义
│   │   ├── routes.ts
│   │   ├── apiEndpoints.ts
│   │   └── config.ts
│   ├── styles/              # 全局样式
│   │   ├── globals.css
│   │   ├── variables.css
│   │   └── mixins.scss
│   ├── assets/              # 静态资源
│   │   ├── images/
│   │   ├── icons/
│   │   └── fonts/
│   ├── App.tsx
│   └── main.tsx
├── tests/                   # 集成测试
│   ├── e2e/
│   └── fixtures/
├── public/
├── .eslintrc.js
├── .prettierrc
├── tsconfig.json
├── vite.config.ts
├── package.json
└── README.md

3.2 目录约束规则

⚠️ 重要约束:
  • 禁止在 src 根目录直接创建组件文件
  • 每个组件必须有独立文件夹,包含组件、样式、类型、测试文件
  • 工具函数按功能模块分组,禁止创建超过 500 行的工具文件
  • API 服务必须按业务领域拆分,禁止创建万能 service

4. React/Vue 组件模板规范

4.1 React 组件标准模板

/**
 * @file UserProfile.tsx
 * @description 用户个人资料展示组件
 * @author AI Code Generator
 * @created 2026-03-13
 */

import React, { FC, memo } from 'react';
import PropTypes from 'prop-types';
import styles from './UserProfile.module.css';
import { UserProfileProps, UserData } from './UserProfile.types';
import { Avatar, Card, Skeleton } from '@/components/ui';
import { formatDate } from '@/utils/format';

/**
 * UserProfile 组件 Props 默认值
 */
const defaultProps: Partial<UserProfileProps> = {
    showJoinDate: true,
    size: 'medium',
    onEditClick: undefined
};

/**
 * UserProfile - 用户个人资料展示组件
 * 
 * @param {UserProfileProps} props - 组件属性
 * @returns {JSX.Element} 渲染的用户资料组件
 * 
 * @example
 * <UserProfile 
 *   userId="123" 
 *   showJoinDate={true}
 *   onEditClick={() => handleEdit()}
 * />
 */
export const UserProfile: FC<UserProfileProps> = memo((props) => {
    // 合并默认 props
    const mergedProps = { ...defaultProps, ...props };
    const { userId, showJoinDate, size, onEditClick } = mergedProps;

    // 自定义 Hooks 调用
    const { data, loading, error } = useUser(userId);

    // 加载状态
    if (loading) {
        return <Skeleton height={200} />;
    }

    // 错误状态
    if (error) {
        return <div className={styles.error}>{error}</div>;
    }

    // 主渲染逻辑
    return (
        <Card className={styles[container]}>
            <Avatar 
                src={data?.avatar} 
                size={size}
                alt={data?.name} 
            />
            
            <div className={styles.info}>
                <h3 className={styles.name}>
                    {data?.name}
                </h3>
                
                {showJoinDate && (
                    <p className={styles.joinDate}>
                        加入于 {formatDate(data?.createdAt)}
                    </p>
                )}
            </div>

            {onEditClick && (
                <button 
                    onClick={onEditClick}
                    className={styles.editBtn}
                >
                    编辑资料
                </button>
            )}
        </Card>
    );
});

// PropTypes 运行时验证(可选,TypeScript 项目可省略)
UserProfile.propTypes = {
    userId: PropTypes.string.isRequired,
    showJoinDate: PropTypes.bool,
    size: PropTypes.oneOf(['small', 'medium', 'large']),
    onEditClick: PropTypes.func
};

// 显示名称
UserProfile.displayName = 'UserProfile';

export default UserProfile;

4.2 React 组件类型定义模板

/**
 * @file UserProfile.types.ts
 * @description UserProfile 组件类型定义
 */

import { CSSProperties } from 'react';

/**
 * 用户数据接口
 */
export interface UserData {
    /** 用户 ID */
    id: string;
    /** 用户名 */
    name: string;
    /** 头像 URL */
    avatar?: string;
    /** 邮箱 */
    email: string;
    /** 创建时间 */
    createdAt: string;
    /** 更新时间 */
    updatedAt: string;
}

/**
 * 组件尺寸枚举
 */
export type ComponentSize = 'small' | 'medium' | 'large';

/**
 * UserProfile 组件 Props 接口
 */
export interface UserProfileProps {
    /** 用户 ID(必填) */
    userId: string;
    /** 是否显示加入日期(可选,默认 true) */
    showJoinDate?: boolean;
    /** 组件尺寸(可选,默认 'medium') */
    size?: ComponentSize;
    /** 编辑按钮点击回调(可选) */
    onEditClick?: () => void;
    /** 自定义样式类名(可选) */
    className?: string;
    /** 自定义内联样式(可选) */
    style?: CSSProperties;
}

/**
 * 内部状态接口(如需要)
 */
export interface UserProfileState {
    isEditing: boolean;
    isLoading: boolean;
}

4.3 Vue 3 组件标准模板

<!--
 * @file UserProfile.vue
 * @description 用户个人资料展示组件
 * @author AI Code Generator
 * @created 2026-03-13
-->

<script setup lang="ts">
import { defineProps, defineEmits, computed, ref } from 'vue';
import { UserProfileProps, UserData } from './UserProfile.types';
import { useUser } from '@/hooks/useUser';
import { formatDate } from '@/utils/format';

/**
 * 定义组件 Props
 */
const props = defineProps<UserProfileProps>({
    userId: {
        type: String,
        required: true
    },
    showJoinDate: {
        type: Boolean,
        default: true
    },
    size: {
        type: String as PropType<'small' | 'medium' | 'large'>,
        default: 'medium',
        validator: (value) => ['small', 'medium', 'large'].includes(value)
    }
});

/**
 * 定义组件事件
 */
const emit = defineEmits<{
    edit: []
    update: [data: UserData]
}>();

/**
 * 使用组合式 API
 */
const { data, loading, error } = useUser(toRef(props, 'userId'));

const formattedJoinDate = computed(() => {
    return data.value ? formatDate(data.value.createdAt) : '';
});

const handleEdit = () => {
    emit('edit');
};
</script>

<template>
    <div v-if="loading" class="skeleton">
        <Skeleton height="200" />
    </div>
    
    <div v-else-if="error" class="error">
        {{ error }}
    </div>
    
    <Card v-else class="user-profile">
        <Avatar 
            :src="data?.avatar" 
            :size="size"
            :alt="data?.name" 
        />
        
        <div class="info">
            <h3 class="name">{{ data?.name }}</h3>
            
            <p v-if="showJoinDate" class="join-date">
                加入于 {{ formattedJoinDate }}
            </p>
        </div>

        <button 
            @click="handleEdit"
            class="edit-btn"
        >
            编辑资料
        </button>
    </Card>
</template>

<style module scoped>
/* CSS 样式见第 8 节 */
</style>

5. TypeScript 类型定义规范

5.1 基础类型约束

// ✅ 必须遵守的类型规则

// 1. 禁止使用 any 类型
let data: any; // ❌ 严禁使用
let data: unknown; // ✅ 使用 unknown 代替

// 2. 明确返回值类型
function getUser(id: string) { // ❌ 缺少返回类型
    return { id, name: 'John' };
}

function getUser(id: string): Promise<UserData> { // ✅ 明确返回类型
    return api.get(`/users/${id}`);
}

// 3. 使用接口定义对象结构
type User = { // ❌ 优先使用 interface
    id: string;
    name: string;
};

interface User { // ✅ 使用 interface
    id: string;
    name: string;
}

// 4. 联合类型优于枚举(简单场景)
type Status = 'pending' | 'success' | 'error'; // ✅ 推荐

// 5. 使用 Pick/Omit 进行类型转换
interface UserFull {
    id: string;
    name: string;
    email: string;
    password: string;
}

type UserPublic = Omit<UserFull, 'password'>; // ✅ 排除敏感字段
type UserNameOnly = Pick<UserFull, 'id' | 'name'>; // ✅ 选取部分字段

5.2 API 响应类型模板

/**
 * @file api.types.ts
 * @description 通用 API 类型定义
 */

/**
 * 通用 API 响应结构
 */
export interface ApiResponse<T = any> {
    /** 状态码 */
    code: number;
    /** 消息 */
    message: string;
    /** 数据 */
    data: T;
    /** 时间戳 */
    timestamp: number;
}

/**
 * 分页参数
 */
export interface PaginationParams {
    /** 页码,从 1 开始 */
    page: number;
    /** 每页数量 */
    pageSize: number;
    /** 排序字段 */
    sortBy?: string;
    /** 排序方向 */
    order?: 'asc' | 'desc';
}

/**
 * 分页响应数据
 */
export interface PaginatedResponse<T> {
    /** 数据列表 */
    items: T[];
    /** 总数 */
    total: number;
    /** 总页数 */
    totalPages: number;
    /** 当前页 */
    currentPage: number;
}

/**
 * 异步请求状态
 */
export type RequestStatus = 'idle' | 'loading' | 'success' | 'error';

/**
 * 异步请求结果
 */
export interface AsyncResult<T> {
    data: T | null;
    error: Error | null;
    status: RequestStatus;
    isLoading: boolean;
}

6. API 接口调用层代码模板

6.1 Axios 拦截器配置模板

/**
 * @file interceptors.ts
 * @description Axios 请求/响应拦截器配置
 */

import axios, { 
    AxiosInstance, 
    AxiosRequestConfig, 
    AxiosError,
    AxiosResponse
} from 'axios';
import { ApiResponse } from '@/types/api.types';
import { getToken, clearToken } from '@/utils/auth';
import { refreshToken } from '@/services/authService';

/**
 * 创建 Axios 实例
 */
export const apiClient: AxiosInstance = axios.create({
    baseURL: process.env.VITE_API_BASE_URL || '/api',
    timeout: 15000,
    headers: {
        'Content-Type': 'application/json'
    }
});

/**
 * 请求拦截器
 */
apiClient.interceptors.request.use(
    async (config: AxiosRequestConfig) => {
        // 添加认证 Token
        const token = getToken();
        if (token) {
            config.headers = config.headers || {};
            config.headers.Authorization = `Bearer ${token}`;
        }

        // 添加请求 ID 用于追踪
        config.headers['X-Request-ID'] = generateRequestId();

        return config;
    },
    (error: AxiosError) => Promise.reject(error)
);

/**
 * 响应拦截器
 */
apiClient.interceptors.response.use(
    (response: AxiosResponse<ApiResponse>) => {
        const { data } = response;

        // 统一处理业务错误
        if (data.code !== 200) {
            return Promise.reject(new BusinessError(data.message, data.code));
        }

        return data.data;
    },
    async (error: AxiosError<ApiResponse>) => {
        const originalRequest = error.config;

        // Token 过期处理
        if (error.response?.status === 401 && !originalRequest?._retry) {
            originalRequest._retry = true;

            try {
                const newToken = await refreshToken();
                originalRequest.headers = originalRequest.headers || {};
                originalRequest.headers.Authorization = `Bearer ${newToken}`;
                return apiClient(originalRequest);
            } catch (refreshError) {
                clearToken();
                window.location.href = '/login';
                return Promise.reject(refreshError);
            }
        }

        // 统一错误处理
        const errorMessage = error.response?.data?.message || '网络请求失败';
        showErrorToast(errorMessage);

        return Promise.reject(error);
    }
);

6.2 API Service 层模板

/**
 * @file userService.ts
 * @description 用户相关 API 服务
 */

import { apiClient } from './api';
import { UserData, UserCreateParams, UserUpdateParams } from '@/types/user.types';
import { ApiResponse, PaginatedResponse, PaginationParams } from '@/types/api.types';

/**
 * API 端点常量
 */
const ENDPOINTS = {
    USERS: '/users',
    USER_PROFILE: (id: string) => `/users/${id}`,
    USER_AVATAR: (id: string) => `/users/${id}/avatar`
} as const;

/**
 * 获取用户列表
 * @param params - 分页和筛选参数
 * @returns 分页用户数据
 */
export async function getUserList(
    params: PaginationParams & { keyword?: string }
): Promise<PaginatedResponse<UserData>> {
    return apiClient.get(ENDPOINTS.USERS, { params });
}

/**
 * 获取用户详情
 * @param userId - 用户 ID
 * @returns 用户数据
 */
export async function getUserById(userId: string): Promise<UserData> {
    return apiClient.get(ENDPOINTS.USER_PROFILE(userId));
}

/**
 * 创建用户
 * @param data - 用户创建参数
 * @returns 创建的用户数据
 */
export async function createUser(data: UserCreateParams): Promise<UserData> {
    return apiClient.post(ENDPOINTS.USERS, data);
}

/**
 * 更新用户
 * @param userId - 用户 ID
 * @param data - 用户更新参数
 * @returns 更新后的用户数据
 */
export async function updateUser(
    userId: string, 
    data: UserUpdateParams
): Promise<UserData> {
    return apiClient.put(ENDPOINTS.USER_PROFILE(userId), data);
}

/**
 * 删除用户
 * @param userId - 用户 ID
 */
export async function deleteUser(userId: string): Promise<void> {
    return apiClient.delete(ENDPOINTS.USER_PROFILE(userId));
}

/**
 * 上传用户头像
 * @param userId - 用户 ID
 * @param file - 头像文件
 * @returns 头像 URL
 */
export async function uploadUserAvatar(
    userId: string, 
    file: File
): Promise<string> {
    const formData = new FormData();
    formData.append('avatar', file);

    return apiClient.post(ENDPOINTS.USER_AVATAR(userId), formData, {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    });
}

7. 状态管理规范模板

7.1 Redux Toolkit Slice 模板

/**
 * @file userSlice.ts
 * @description 用户状态管理 Slice
 */

import { 
    createSlice, 
    createAsyncThunk, 
    PayloadAction,
    createSelector
} from '@reduxjs/toolkit';
import { UserData } from '@/types/user.types';
import { getUserById, updateUser } from '@/services/userService';

/**
 * 状态接口
 */
interface UserState {
    current: UserData | null;
    loading: boolean;
    error: string | null;
    lastUpdated: number | null;
}

/**
 * 初始状态
 */
const initialState: UserState = {
    current: null,
    loading: false,
    error: null,
    lastUpdated: null
};

/**
 * 异步 Thunk: 获取用户详情
 */
export const fetchUser = createAsyncThunk(
    'user/fetchUser',
    async (userId: string) => {
        return await getUserById(userId);
    }
);

/**
 * 异步 Thunk: 更新用户
 */
export const patchUser = createAsyncThunk(
    'user/updateUser',
    async ({ userId, data }: { userId: string; data: Partial<UserData> }) => {
        return await updateUser(userId, data);
    }
);

/**
 * 创建 Slice
 */
const userSlice = createSlice({
    name: 'user',
    initialState,
    reducers: {
        clearUser: (state) => {
            state.current = null;
            state.error = null;
        },
        setError: (state, action: PayloadAction<string>) => {
            state.error = action.payload;
            state.loading = false;
        }
    },
    extraReducers: (builder) => {
        builder
            // 获取用户
            .addCase(fetchUser.pending, (state) => {
                state.loading = true;
                state.error = null;
            })
            .addCase(fetchUser.fulfilled, (state, action) => {
                state.current = action.payload;
                state.loading = false;
                state.lastUpdated = Date.now();
            })
            .addCase(fetchUser.rejected, (state, action) => {
                state.loading = false;
                state.error = action.error.message || 'Failed to fetch user';
            })
            // 更新用户
            .addCase(patchUser.fulfilled, (state, action) => {
                state.current = action.payload;
                state.lastUpdated = Date.now();
            });
    }
});

/**
 * 导出 Actions
 */
export const { clearUser, setError } = userSlice.actions;

/**
 * 选择器
 */
export const selectCurrentUser = (state: { user: UserState }) => 
    state.user.current;

export const selectIsLoading = (state: { user: UserState }) => 
    state.user.loading;

export const selectUserError = (state: { user: UserState }) => 
    state.user.error;

// 记忆化选择器
export const selectUserName = createSelector(
    [selectCurrentUser],
    (user) => user?.name || 'Guest'
);

export default userSlice.reducer;

8. CSS/样式代码规范

8.1 CSS Modules 命名规范

/* ✅ CSS Modules 命名约定 */

/* 容器使用 container 或 wrapper */
.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 20px;
}

/* 使用 BEM 风格命名 */
.userProfile { /* Block */
    display: flex;
    flexDirection: column;
}

.userProfile__header { /* Element */
    padding: 20px;
    borderBottom: 1px solid var(--border-color);
}

.userProfile__avatar--large { /* Modifier */
    width: 120px;
    height: 120px;
}

/* 状态前缀 */
.isLoading {
    opacity: 0.6;
    pointerEvents: none;
}

.isDisabled {
    cursor: not-allowed;
    opacity: 0.5;
}

.hasError {
    borderColor: var(--error-color);
}

/* 布局相关 */
.flexCenter {
    display: flex;
    alignItems: center;
    justifyContent: center;
}

.gridLayout {
    display: grid;
    gap: 20px;
}

/* 间距使用 scale */
.mt1 { marginTop: 8px; }
.mt2 { marginTop: 16px; }
.mt3 { marginTop: 24px; }
.mt4 { marginTop: 32px; }

8.2 TailwindCSS 使用规范

<!-- ✅ TailwindCSS 最佳实践 -->

<!-- 1. 使用组件抽象复杂样式 -->
<!-- ❌ 避免 -->
<div className="flex items-center justify-between p-4 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200">
<!-- ✅ 推荐 -->
<Card variant="hoverable">

<!-- 2. 响应式设计从移动优先 -->
<div className="w-full md:w-1/2 lg:w-1/3 px-4">

<!-- 3. 使用 CSS 变量保持主题一致性 -->
<div className="bg-primary text-primary-foreground">

<!-- 4. 条件样式使用 clsx 或 classnames -->
import clsx from 'clsx';

<button 
    className={clsx(
        'px-4 py-2 rounded font-medium',
        isActive && 'bg-blue-500 text-white',
        isDisabled && 'opacity-50 cursor-not-allowed'
    )}
>

9. 单元测试代码模板

9.1 React 组件测试模板

/**
 * @file UserProfile.test.tsx
 * @description UserProfile 组件单元测试
 */

import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { UserProfile } from './UserProfile';
import { useUser } from '@/hooks/useUser';

// Mock Hooks
vi.mock('@/hooks/useUser', () => ({
    useUser: vi.fn()
}));

describe('UserProfile Component', () => {
    const mockUser = {
        id: '123',
        name: 'John Doe',
        email: 'john@example.com',
        avatar: 'https://example.com/avatar.jpg',
        createdAt: '2024-01-01T00:00:00Z'
    };

    beforeEach(() => {
        vi.clearAllMocks();
    });

    it('renders loading state initially', () => {
        // Arrange
        (useUser as any).mockReturnValue({
            data: null,
            loading: true,
            error: null
        });

        // Act
        render(<UserProfile userId="123" />);

        // Assert
        expect(screen.queryByRole('progressbar')).toBeInTheDocument();
    });

    it('displays user data when loaded successfully', async () => {
        // Arrange
        (useUser as any).mockReturnValue({
            data: mockUser,
            loading: false,
            error: null
        });

        // Act
        render(<UserProfile userId="123" />);

        // Assert
        await waitFor(() => {
            expect(screen.getByText('John Doe')).toBeInTheDocument();
        });
        
        expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
    });

    it('shows error message when fetch fails', () => {
        // Arrange
        (useUser as any).mockReturnValue({
            data: null,
            loading: false,
            error: 'Failed to load user'
        });

        // Act
        render(<UserProfile userId="123" />);

        // Assert
        expect(screen.getByText('Failed to load user')).toBeInTheDocument();
    });

    it('calls onEditClick when edit button is clicked', async () => {
        // Arrange
        const handleEdit = vi.fn();
        (useUser as any).mockReturnValue({
            data: mockUser,
            loading: false,
            error: null
        });

        // Act
        render(<UserProfile userId="123" onEditClick={handleEdit} />);
        
        await waitFor(() => {
            expect(screen.getByText('编辑资料')).toBeInTheDocument();
        });

        fireEvent.click(screen.getByText('编辑资料'));

        // Assert
        expect(handleEdit).toHaveBeenCalledTimes(1);
    });

    it('does not show join date when showJoinDate is false', async () => {
        // Arrange
        (useUser as any).mockReturnValue({
            data: mockUser,
            loading: false,
            error: null
        });

        // Act
        render(<UserProfile userId="123" showJoinDate={false} />);

        // Assert
        await waitFor(() => {
            expect(screen.queryByText(/加入于/)).not.toBeInTheDocument();
        });
    });
});

9.2 工具函数测试模板

/**
 * @file formatDate.test.ts
 * @description formatDate 工具函数测试
 */

import { describe, it, expect } from 'vitest';
import { formatDate } from './formatDate';

describe('formatDate utility', () => {
    it('formats ISO date string correctly', () => {
        const input = '2024-01-15T10:30:00Z';
        const expected = '2024-01-15';
        
        expect(formatDate(input)).toBe(expected);
    });

    it('returns empty string for null input', () => {
        expect(formatDate(null)).toBe('');
    });

    it('returns empty string for undefined input', () => {
        expect(formatDate(undefined)).toBe('');
    });

    it('handles invalid date gracefully', () => {
        expect(formatDate('invalid-date')).toBe('');
    });
});

10. 代码质量约束规则

10.1 ESLint 配置规则

// .eslintrc.js - AI 生成代码必须遵守的规则
module.exports = {
    root: true,
    env: {
        browser: true,
        es2022: true,
        node: true
    },
    extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/recommended',
        'plugin:react/recommended',
        'plugin:react-hooks/recommended',
        'prettier'
    ],
    parser: '@typescript-eslint/parser',
    plugins: ['@typescript-eslint', 'react', 'import'],
    rules: {
        // 强制规则 - 违反将导致构建失败
        'no-unused-vars': 'off',
        '@typescript-eslint/no-unused-vars': ['error', { 
            argsIgnorePattern: '^_' 
        }],
        
        'no-explicit-any': 'off',
        '@typescript-eslint/no-explicit-any': 'error',
        
        'react/prop-types': 'off',
        'react/react-in-jsx-scope': 'off',
        
        'import/order': ['error', {
            groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
            'newlines-between': 'always',
            alphabetize: { order: 'asc' }
        }],

        // 复杂度限制
        'max-depth': ['warn', 4],
        'max-nested-callbacks': ['warn', 3],
        'max-params': ['warn', 4],
        'complexity': ['warn', 20],
        
        // 代码长度限制
        'max-lines-per-function': ['warn', {
            max: 100,
            skipBlankLines: true,
            skipComments: true
        }],
        
        'max-lines': ['warn', {
            max: 500,
            skipBlankLines: true,
            skipComments: true
        }]
    }
};

10.2 代码审查检查清单

检查项 检查方式 通过标准 优先级
类型安全 TypeScript 编译 零类型错误 P0
ESLint 规则 eslint --max-warnings=0 零错误零警告 P0
格式化 Prettier 检查 格式完全一致 P0
单元测试覆盖率 Vitest/Jest 行覆盖率 ≥80% P0
组件可访问性 axe-core WCAG AA 合规 P1
性能指标 Lighthouse Performance ≥90 P1
Bundle 大小 webpack-bundle-analyzer 单 chunk ≤500KB P1

11. Git 工作流规范

11.1 分支命名规范

# Git 分支命名约定

# 主分支
main          # 生产环境
develop       # 开发环境

# 功能分支 (feature)
feature/user-authentication
feature/order-management
feature/payment-integration

# 修复分支 (fix)
fix/login-bug
fix/memory-leak
fix/api-timeout

# 热修复分支 (hotfix)
hotfix/critical-security-patch
hotfix/production-crash

# 发布分支 (release)
release/v1.2.0
release/v2.0.0-beta

# 实验分支 (experiment)
exp/new-routing-strategy
exp/performance-optimization

11.2 Commit Message 规范

# Conventional Commits 规范
# <type>(<scope>): <subject>

# Type 类型:
feat:     # 新功能
fix:      # Bug 修复
docs:     # 文档变更
style:    # 代码格式 (不影响代码运行)
refactor: # 重构 (既不是新增功能也不是修改 bug)
perf:     # 性能优化
test:     # 测试相关
chore:    # 构建过程或辅助工具变动
ci:       # CI 配置文件
build:    # 影响构建系统或外部依赖
revert:   # 回滚提交

# 示例:
feat(user): add user profile component
fix(api): resolve timeout issue in getUserById
docs(readme): update installation instructions
refactor(auth): simplify token refresh logic
perf(render): optimize list rendering with virtualization
test(user): add unit tests for UserProfile component
chore(deps): upgrade react to v18.2.0
ci(github): add automated testing workflow

# 完整的 Commit Message 格式:
feat(user): add user profile component

Add UserProfile component with avatar display and edit functionality.
- Create UserProfile component with TypeScript types
- Add CSS Modules styling
- Implement loading and error states
- Add comprehensive unit tests

Closes #123
Related to #456

12. AI Coding Prompt 模板

12.1 组件生成 Prompt 模板

"""
你是一位资深前端工程师,请根据以下需求生成符合企业规范的 React 组件代码。

【需求描述】
{用户需求描述}

【技术要求】
1. 使用 React 18 + TypeScript 5
2. 遵循以下规范:
   - 文件命名:PascalCase.tsx
   - 组件结构:函数组件 + memo 优化
   - 样式方案:CSS Modules
   - 类型定义:独立的 .types.ts 文件
   - 必须包含完整的 JSDoc 注释

【代码约束】
1. 禁止使用 any 类型
2. 所有 Props 必须有明确的类型定义
3. 必须处理 loading 和 error 状态
4. 必须包含 PropTypes 验证(可选)
5. 函数最大行数不超过 100 行
6. 组件嵌套深度不超过 4 层

【输出要求】
请按以下顺序输出完整代码:
1. 组件类型定义文件 (*.types.ts)
2. 组件实现文件 (*.tsx)
3. 组件样式文件 (*.module.css)
4. 组件测试文件 (*.test.tsx)

【质量标准】
- ESLint 零错误零警告
- 单元测试覆盖率 ≥80%
- 符合 WCAG 2.1 AA 可访问性标准
- 响应式设计支持移动端

请开始生成代码:
"""

12.2 API Service 生成 Prompt 模板

"""
你是一位后端 API 集成专家,请根据以下 API 文档生成 TypeScript 服务层代码。

【API 端点信息】
Base URL: {BASE_URL}
Endpoints:
{API 端点列表}

【数据类型】
{API 请求/响应数据结构}

【技术要求】
1. 使用 Axios 作为 HTTP 客户端
2. 遵循以下规范:
   - 服务文件命名:*Service.ts
   - 所有方法必须是 async 函数
   - 明确的泛型类型定义
   - 统一的错误处理机制
   - 请求/响应拦截器已配置

【代码约束】
1. 端点路径必须定义为常量
2. 每个方法必须有完整的 JSDoc
3. 必须处理常见错误场景(401, 403, 404, 500)
4. 支持请求取消(AbortController)
5. 添加适当的超时配置

【输出要求】
1. 类型定义文件 (types/*.types.ts)
2. API 服务实现文件 (services/*Service.ts)
3. 使用示例代码

请开始生成代码:
"""

13. CI/CD 集成规范

13.1 GitHub Actions 工作流模板

# .github/workflows/frontend-ci.yml
name: Frontend CI/CD Pipeline

on:
  push:
    branches: ['main', 'develop']
  pull_request:
    branches: ['main', 'develop']

jobs:
  lint-and-typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run ESLint
        run: npm run lint -- --max-warnings=0
      
      - name: Type Check
        run: npm run typecheck

  test:
    runs-on: ubuntu-latest
    needs: lint-and-typecheck
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests with coverage
        run: npm run test:coverage
      
      - name: Upload coverage reports
        uses: codecov/codecov-action@v3
        with:
          files: './coverage/lcov.info'

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build application
        run: npm run build
      
      - name: Analyze bundle size
        run: npm run analyze
      
      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: build-dist
          path: dist/

  deploy:
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'
    environment: production
    steps:
      - name: Deploy to K8s
        run: |
          kubectl apply -f k8s/deployment.yaml
          kubectl rollout status deployment/frontend

13.2 Docker 配置模板

# Dockerfile - 多阶段构建优化镜像大小
# Stage 1: Build
FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

# Stage 2: Production
FROM nginx:alpine

COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]