1. 文档概述
🎯 文档目的:本模板为前端研发岗位 Agent 提供标准化的技术方案输出框架,确保在端到端研发自动化系统中,前端技术方案的一致性、完整性和可执行性。
1.1 适用范围
- 适用于基于 OpenClaw + Claude Code 的端到端研发自动化系统中的前端研发环节
- 适用于 Web 应用、H5 页面、管理后台等前端项目开发
- 支持人机协同模式,AI 生成初稿 + 人工审核优化
1.2 输入输出定义
📥 输入物
- 产品需求文档 (PRD)
- UI/UX 设计稿 (Figma/Sketch)
- 后端技术方案设计文档
- 前后端 API 接口协议
- 业务流程图
📤 输出物
- 前端技术方案设计文档
- 页面结构拆解图
- 组件设计清单
- 交互流程说明
- 技术风险评估报告
2. 技术架构设计
2.1 技术栈选型
| 类别 |
技术选项 |
版本要求 |
选型理由 |
| 核心框架 |
React / Vue 3 |
React 18+ / Vue 3.3+ |
组件化开发、生态完善、性能优异 |
| 语言 |
TypeScript |
5.0+ |
类型安全、代码智能提示、降低运行时错误 |
| 状态管理 |
Zustand / Pinia |
最新稳定版 |
轻量级、易用、DevTools 支持 |
| 路由 |
React Router / Vue Router |
v6+ / v4+ |
成熟稳定、支持懒加载 |
| UI 组件库 |
Ant Design / Element Plus |
最新稳定版 |
企业级组件、主题定制能力强 |
| 构建工具 |
Vite |
5.0+ |
极速启动、热更新、打包优化 |
| HTTP 客户端 |
Axios / TanStack Query |
最新稳定版 |
请求拦截、缓存策略、错误处理 |
| 测试框架 |
Vitest + Testing Library |
最新稳定版 |
快速执行、覆盖率报告、组件测试 |
2.2 整体架构图
用户层
↓
展示层 (Pages/Layouts)
↓
组件层 (Components)
↓
状态管理层 (Store)
↓
服务层 (API/Hooks)
↓
后端服务
2.3 目录结构规范
src/
├── assets/
│ ├── images/
│ ├── fonts/
│ └── styles/
├── components/
│ ├── base/
│ ├── business/
│ └── layouts/
├── pages/
├── hooks/
├── stores/
├── services/
├── utils/
├── types/
├── constants/
├── config/
├── routes/
└── App.tsx
3. 页面结构设计规范
3.1 路由设计规范
路由命名原则:使用 kebab-case,语义清晰,符合 RESTful 风格
const routes: RouteRecordRaw[] = [
{
path: '/',
component: Layout,
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: () => import('@/pages/Dashboard/index.vue'),
meta: {
title: '工作台',
requiresAuth: true,
keepAlive: true
}
},
{
path: 'user-management/:id',
name: 'UserDetail',
component: () => import('@/pages/User/Detail.vue'),
meta: { title: '用户详情' }
}
]
}
]
3.2 页面组件拆分原则
📄 Page 层
- 负责数据获取
- 状态初始化
- 页面级布局
- 路由参数处理
🧩 Section 层
- 页面功能区块
- 独立业务逻辑
- 可复用模块
- 数据转换处理
🔷 Component 层
- 纯 UI 组件
- props/emit 通信
- 无副作用
- 高可复用性
3.3 布局系统
interface LayoutProps {
showHeader?: boolean;
showSidebar?: boolean;
showFooter?: boolean;
sidebarWidth?: string;
}
const breakpoints = {
mobile: '(max-width: 768px)',
tablet: '(max-width: 1024px)',
desktop: '(min-width: 1025px)'
}
4. 组件设计规范
4.1 组件分类体系
| 组件类型 |
职责 |
示例 |
存放位置 |
| 基础组件 |
原子级 UI 元素,无业务逻辑 |
Button, Input, Modal |
components/base/ |
| 业务组件 |
包含业务逻辑的可复用组件 |
UserTable, ProductCard |
components/business/ |
| 复合组件 |
组合多个组件完成复杂功能 |
DataTable, FormWizard |
components/composite/ |
| 布局组件 |
页面结构布局 |
Header, Sidebar, Container |
components/layouts/ |
4.2 组件 API 设计标准
Props 设计原则:单一职责、类型明确、默认值合理、避免双向绑定
interface UserTableProps {
dataSource: UserType[];
columns: ColumnType[];
loading?: boolean;
pagination?: PaginationConfig;
rowSelection?: 'checkbox' | 'radio';
size?: 'small' | 'medium' | 'large';
}
interface UserTableEmits {
(e: 'rowClick', row: UserType): void;
(e: 'selectionChange', selectedRows: UserType[]): void;
(e: 'pageChange', page: number): void;
(e: 'sortChange', sorter: SorterResult): void;
}
4.3 组件文档模板
5. 交互设计规范
5.1 用户操作流程规范
用户触发
→
事件捕获
→
数据验证
→
状态更新
→
UI 反馈
→
异步请求
→
结果处理
5.2 表单交互规范
✅ 验证规则
- 实时验证 + 提交前验证
- 错误信息清晰明确
- 必填项标识明显
- 支持键盘导航
- 自动聚焦首个错误字段
⚡ 用户体验
- 防抖处理 (300ms)
- 提交按钮 Loading 状态
- 防止重复提交
- 表单自动保存草稿
- 支持快捷键提交 (Ctrl+S)
5.3 反馈机制
| 反馈类型 |
使用场景 |
持续时间 |
组件 |
| Success |
操作成功 |
3s 自动关闭 |
Message/Notification |
| Error |
操作失败/异常 |
手动关闭 |
Modal/Notification |
| Warning |
警告提示 |
5s 自动关闭 |
Message |
| Loading |
异步操作进行中 |
操作完成 |
Spin/Button.loading |
| Info |
信息提示 |
4s 自动关闭 |
Message |
5.4 空状态处理
interface EmptyStateProps {
image?: string;
description?: string;
action?: {
text: string;
onClick: () => void;
};
}
const EMPTY_STATES = {
NO_DATA: '暂无数据',
NO_SEARCH_RESULT: '未找到相关结果',
NO_PERMISSION: '暂无访问权限',
NETWORK_ERROR: '网络异常,请重试'
}
6. 状态管理规范
6.1 状态分层策略
🔸 组件状态
- UI 交互状态
- 本地临时数据
- 受控组件值
- 动画状态
6.2 Store 设计规范
interface UserStore {
userInfo: UserInfo | null;
permissions: string[];
isLoading: boolean;
fetchUserInfo: () => Promise<void>;
updateUserInfo: (data: Partial<UserInfo>) => void;
logout: () => void;
hasPermission: (permission: string) => boolean;
}
const useUserStore = create<UserStore>((set, get) => ({
userInfo: null,
permissions: [],
isLoading: false,
fetchUserInfo: async () => {
set({ isLoading: true });
try {
const data = await api.getUserInfo();
set({ userInfo: data, permissions: data.permissions });
} finally {
set({ isLoading: false });
}
},
hasPermission: (permission) =>
get().permissions.includes(permission)
}))
6.3 服务端状态管理
推荐使用 TanStack Query:自动缓存、背景更新、重试机制、请求去重
export function useUserList(params: UserQueryParams) {
return useQuery({
queryKey: ['users', params],
queryFn: () => api.getUserList(params),
staleTime: 5 * 60 * 1000,
retry: 2,
retryDelay: attemptIndex => Math.min(1000 * Math.pow(2, attemptIndex), 30000)
});
}
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: api.updateUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
}
});
}
7. API 接口对接规范
7.1 HTTP 请求封装
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 30000,
headers: {
'Content-Type': 'application/json'
}
});
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
config.headers['X-Request-ID'] = generateRequestId();
return config;
},
(error) => Promise.reject(error)
);
apiClient.interceptors.response.use(
(response) => {
const { code, data, message } = response.data;
if (code === 200) {
return data;
}
handleBusinessError(code, message);
return Promise.reject(new Error(message));
},
(error) => {
if (error.response?.status === 401) {
handleUnauthorized();
}
return Promise.reject(error);
}
);
7.2 API 服务层组织
import apiClient from '@/utils/request';
import type { UserType, UserQueryParams } from '@/types/user';
export const userService = {
getUserList: (params: UserQueryParams) => {
return apiClient.get<UserType[]>('/api/users', { params });
},
getUserById: (id: string) => {
return apiClient.get<UserType>(`/api/users/${id}`);
},
createUser: (data: CreateUserDTO) => {
return apiClient.post<UserType>('/api/users', data);
},
updateUser: (id: string, data: UpdateUserDTO) => {
return apiClient.put<UserType>(`/api/users/${id}`, data);
},
deleteUser: (id: string) => {
return apiClient.delete(`/api/users/${id}`);
}
};
7.3 接口响应类型定义
export interface ApiResponse<T> {
code: number;
data: T;
message: string;
timestamp: number;
}
export interface PageResponse<T> {
list: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}
export interface QueryParams {
page?: number;
pageSize?: number;
sortField?: string;
sortOrder?: 'ascend' | 'descend';
[key: string]: any;
}
8. 开发环境与工程配置
8.1 环境变量配置
VITE_APP_TITLE="开发环境"
VITE_API_BASE_URL="/api/dev"
VITE_WS_URL="ws://localhost:8080/ws"
VITE_APP_TITLE="生产环境"
VITE_API_BASE_URL="/api/prod"
VITE_WS_URL="wss://example.com/ws"
8.2 Vite 配置
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
},
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
ui: ['element-plus']
}
}
},
chunkSizeWarningLimit: 1500
}
});
8.3 脚本命令
{
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx --fix",
"type-check": "vue-tsc --noEmit",
"test": "vitest",
"test:coverage": "vitest --coverage",
"test:e2e": "playwright test"
}
}
9. 代码规范与质量保障
9.1 ESLint 配置
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended'
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
rules: {
'no-console': 'warn',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'vue/multi-word-component-names': 'off'
}
}
9.2 命名规范
| 类型 |
命名规则 |
示例 |
| 文件/文件夹 |
PascalCase (组件) / camelCase (其他) |
UserTable.vue, userService.ts |
| 组件名 |
PascalCase,多单词 |
UserTable, ProductCard |
| 变量/函数 |
camelCase |
userInfo, getUserList |
| 常量 |
UPPER_SNAKE_CASE |
MAX_RETRY_COUNT, API_TIMEOUT |
| 类型/接口 |
PascalCase |
UserType, ApiResponse |
| CSS 类名 |
kebab-case |
user-table, btn-primary |
9.3 Git 工作流
分支策略:采用 Git Flow 工作流,支持 CI/CD 自动化
main
develop
feature/*
release/*
hotfix/*
<type>(<scope>): <subject>
feat:
fix:
docs:
style:
refactor:
test:
chore:
feat(user): 添加用户批量删除功能
fix(auth): 修复登录 token 过期问题
refactor(api): 重构 HTTP 请求拦截器
10. 测试策略
10.1 测试金字塔
🔺
少量 E2E 测试
适量集成测试
大量单元测试
10.2 单元测试规范
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { userService } from '@/services/user';
import apiClient from '@/utils/request';
vi.mock('@/utils/request');
describe('userService', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should fetch user list successfully', async () => {
const mockUsers = [
{ id: '1', name: 'John' },
{ id: '2', name: 'Jane' }
];
vi.mocked(apiClient.get).mockResolvedValue(mockUsers);
const result = await userService.getUserList({ page: 1 });
expect(result).toEqual(mockUsers);
expect(apiClient.get).toHaveBeenCalledWith('/api/users', {
params: { page: 1 }
});
});
it('should handle error when fetch fails', async () => {
vi.mocked(apiClient.get).mockRejectedValue(new Error('Network Error'));
await expect(userService.getUserList({}))
.rejects.toThrow('Network Error');
});
});
10.3 组件测试
import { render, screen, fireEvent } from '@testing-library/vue';
import UserTable from '@/components/business/UserTable.vue';
describe('UserTable', () => {
const defaultProps = {
dataSource: [
{ id: '1', name: 'John', email: 'john@example.com' },
{ id: '2', name: 'Jane', email: 'jane@example.com' }
],
columns: [
{ key: 'name', title: '姓名' },
{ key: 'email', title: '邮箱' }
]
};
it('renders user data correctly', () => {
render(UserTable, { props: defaultProps });
expect(screen.getByText('John')).toBeInTheDocument();
expect(screen.getByText('jane@example.com')).toBeInTheDocument();
});
it('emits rowClick event when row is clicked', async () => {
const { emitted } = render(UserTable, { props: defaultProps });
const firstRow = screen.getByText('John').closest('tr');
await fireEvent.click(firstRow);
expect(emitted().rowClick).toHaveLength(1);
expect(emitted().rowClick[0]).toEqual([defaultProps.dataSource[0]]);
});
});
10.4 E2E 测试
import { test, expect } from '@playwright/test';
test.describe('User Management', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login');
await page.fill('[name="username"]', 'admin');
await page.fill('[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
test('should create new user successfully', async ({ page }) => {
await page.goto('/users');
await page.click('text=新建用户');
await page.fill('[name="name"]', 'Test User');
await page.fill('[name="email"]', 'test@example.com');
await page.click('button[type="submit"]');
await expect(page.locator('.ant-message-success')).toBeVisible();
await expect(page.getByText('Test User')).toBeVisible();
});
});
11. 部署与监控
11.1 Docker 配置
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
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;"]
11.2 Nginx 配置
server {
listen 80;
server_name example.com;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/plain application/javascript text/css application/json;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://backend:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
11.3 K8S 部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: registry.example.com/frontend:v1.0.0
ports:
- containerPort: 80
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 80
type: ClusterIP
11.4 监控指标
📊 性能监控
- FCP (First Contentful Paint)
- LCP (Largest Contentful Paint)
- FID (First Input Delay)
- CLS (Cumulative Layout Shift)
- API 响应时间
⚠️ 错误监控
- JS 运行时错误
- 资源加载失败
- API 请求错误
- 白屏检测
- 性能降级告警
12. 人机协同工作流程
12.1 AI Coding 工作流
PRD 输入
→
Claude Code
生成技术方案
→
人工审核
→
AI 生成代码
→
人工 Review
→
自动化测试
→
CI/CD 部署
12.2 人机协同检查清单
| 阶段 |
AI 负责 |
人工负责 |
| 技术方案 |
生成初稿、组件拆分、API 设计 |
架构评审、技术选型确认、风险评估 |
| 代码开发 |
生成组件代码、单元测试 |
代码 Review、边界情况处理、性能优化 |
| 测试验证 |
生成测试用例、执行自动化测试 |
探索性测试、用户体验验证、验收测试 |
| 部署上线 |
生成部署配置、执行 CI/CD |
灰度发布决策、线上监控、应急响应 |
12.3 Prompt 工程最佳实践
高效 Prompt 模板:角色设定 + 任务描述 + 约束条件 + 输出格式 + 示例
You are an expert front-end architect with 10+ years of experience.
Task: Generate a comprehensive front-end technical solution for [FEATURE_NAME]
Requirements:
1. Use Vue 3 + TypeScript + Vite stack
2. Follow enterprise-level code standards
3. Include component design, state management, API integration
4. Consider performance optimization and accessibility
Output Format:
- Technical architecture diagram
- Component hierarchy
- Directory structure
- Key code examples
- Risk assessment
Context:
- PRD: [Attach PRD summary]
- API Docs: [Attach API specification]
- UI Design: [Attach Figma link]
📎 附录
附录 A: 常用工具库推荐
工具类
- lodash-es - 工具函数
- dayjs - 日期处理
- axios - HTTP 客户端
- qs - 查询字符串解析
UI 增强
- @vueuse/core - Composition API 工具集
- animate.css - 动画库
- echarts - 图表库
- virtual-scroller - 虚拟滚动
开发工具
- prettier - 代码格式化
- husky - Git hooks
- commitlint - Commit 规范
- size-limit - 包大小限制
附录 B: 参考资源
- Vue 3 官方文档: https://vuejs.org/
- React 官方文档: https://react.dev/
- TypeScript 手册: https://www.typescriptlang.org/docs/
- Web 性能最佳实践: https://web.dev/performance/
- WAI-ARIA 无障碍指南: https://www.w3.org/WAI/ARIA/apg/