---
name: frontend-main
description: >
  当需要前端页面/组件的设计与实现时使用，统一遵循 Lark 设计系统与前端架构规范，
  覆盖视觉设计与交互开发。也用于前端原型（Demo/Mock 页面）产出。
  触发短语：前端开发、实现页面、创建组件、前端原型、Demo、mock 页面、
  implement page、create component、frontend prototype。
---

# 前端功能技能

## 定位

按文档驱动完成前端设计与实现，确保契约一致、i18n 完整、可验证。
也支持原型模式（仅前端 + mock 数据，不接后端）。

## 模式判断

- **正式模式**（默认）：接真实后端，完整 i18n，遵循 API 契约
- **原型模式**（用户提到"原型/Demo/先看效果/不接后端/先用假数据"）：仅前端代码，mock 数据，不改契约

## 快速守则

- 文档为事实来源，未经指令不得改动外部契约（接口/字段/语义）
- 需求改动时，先更新 `docs/modules/{module}/` 对应文档（01-10，按需），再实现
- 所有可见文案必须 i18n（原型模式下新页面允许先硬编码中文，正式实现时补齐）
- 遵循 Lark 设计系统：颜色/渐变/阴影/字体来源统一，表格用原生 `<table>`，每页主按钮 ≤ 1-2 个
- 组件不直接写业务逻辑，API 调用经 hooks/services
- 默认最小改动，提供最小可验证步骤
- **写前置自检**：碰到 Tailwind border / shadcn Table / 表单字段类型新增 / clipboard 复制 / `Content-Type: text/plain` 中文场景前，先翻 [`references/frontend-pitfalls.md`](./references/frontend-pitfalls.md) 的 §1-§7 + §8 checklist 避坑
- **自己跑 L2 验证**：完成前端改动后**必须**起 dev server + Playwright MCP 跑业务流程（不是写完就报完成）——具体姿势见 [`../test-frontend/references/mcp-remote-execution.md`](../test-frontend/references/mcp-remote-execution.md)

## 工作流（正式模式）

1) 从 PRD/验收提取关键用户路径 → 冒烟路径清单
2) 从 UI 交互规范提取页面/组件/交互
3) 对齐 API 契约与数据需求 → 缺口清单
4) 实现页面与组件（遵循设计系统），调用通过 hooks/services
5) 补齐 i18n、错误/加载/成功三态
6) 编写测试或最小验证说明
7) **开发后自检（Self-Review）**：代码实现完成后、提交 PR 前，执行下方自检流程

## 工作流（原型模式）

1) 明确原型范围：页面清单、关键路径、演示要点
2) 选择粒度：低保真（结构流）或高保真（接近上线视觉）
3) 实现页面与交互，数据内联或放 `mock.ts`
   - 禁止发起真实后端请求
   - 假数据需具备业务语义（禁止 `aaa`/`123`）
   - 如有 `07-api.md`，字段命名尽量贴近
4) 自检：关键路径可走通 + 不依赖后端服务

## 设计与视觉规则

- 禁用非 Lark 颜色/自定义渐变/阴影/圆角体系
- 动效以页面进入和分段揭示为主，数量/强度受控
- 优先复用现有组件，未定义规范时提建议而非自创样式
- 页面标题栏统一：`bg-white + border-b`，高度 `h-12`，左侧 `typography.h4`，右侧操作 `size="sm"`

### Tailwind v4 边框红线（必读）

⚠️ **项目用 Tailwind v4**（`frontend/package.json` 里 `"tailwindcss": "^4"`）。
v4 默认 `border-t` / `border-b` / `border` **不带颜色 = `currentColor`（继承文字色 = 黑色）**，与 v3（默认 gray-200）不同。

**禁止**：

```tsx
<div className="border-t" />               // ❌ 黑色边框
<tr className="border-t hover:bg-gray-50">  // ❌
<div className="border-b border-blue-600" />  // ❌（颜色对，但不是项目 token）
```

**必须**：

```tsx
<div className="border border-[#e5e6eb]" />            // ✅ 卡片/输入框
<tr className="border-b border-[#f2f3f5]">             // ✅ 表格行间（更浅）
<div className="border-b-2 border-[#3370ff]" />        // ✅ tab 主色
// 或参考项目惯例（system-roles/page.tsx）用 inline style：
<thead style={{ backgroundColor: '#f7f8fa', borderBottom: '1px solid #e5e6eb' }}>
```

### Lark 设计 token 速查（直接复制）

| 用途 | 值 |
|---|---|
| 卡片/输入框边框 | `#e5e6eb` |
| 表格行间分隔（更浅） | `#f2f3f5` |
| 边框 hover | `#c9cdd4` |
| 主色（按钮、tab、链接） | `#3370ff` |
| 主色 hover | `#1e5eff` |
| thead / row hover 背景 | `#f7f8fa` |
| 卡片背景 | `#ffffff` |
| 文字主 / 次 / 三级 / disabled | `#1f2329` / `#646a73` / `#8f959e` / `#bbbfc4` |
| 成功 / 错误 / 警告 / 信息 | `#00b42a` / `#f53f3f` / `#ff7d00` / `#3370ff` |

完整定义见 `frontend/src/styles/theme.ts`，可直接 `import { colors } from '@/styles/theme'` 使用。

### 自检命令（写完前端后必跑一次）

```bash
# 找出 className 里写了 border 但没有指定项目颜色的位置
grep -rEn 'className="[^"]*\bborder(-[trbl])?(-[0-9]+)?( |\")' \
  src/app/\(modules\)/<your-module>/ \
  | grep -vE 'border-(\[#|\(--|gray|blue|red|green|yellow|purple|pink|orange|slate|zinc|neutral|stone|amber|emerald|teal|cyan|sky|indigo|violet|fuchsia|rose)' \
  | head
```

有匹配 → 该处会渲染**黑色边框**，必须加颜色。

详见 `.learnings/2026-04-30-tailwind-v4-black-borders.md`。

## 架构规则

- 页面组件负责 UI，不直接写业务逻辑
- API 调用遵循 `frontend/src/lib/api-client.ts` 统一响应格式，默认解包返回 `data`
- 无明确约定时参考 `frontend/src/services/api/approval.ts` 模式
- 查询与状态（分页/筛选/排序/搜索/缓存）优先通用封装

---

## 开发后自检（Self-Review）

代码写完后、提交 PR 前，**必须**执行以下自检。目标是在 reviewer 之前自己发现 80% 的问题。

### 自检流程

#### Step 1: 契约与类型一致性

| 检查项 | 说明 | 自动验证方式 |
|--------|------|-------------|
| API 调用 ↔ 后端契约 | 请求参数名/类型与 `07-api.md` 或后端 DTO 一致 | grep 前端 service 文件中的 API 调用，对比后端 DTO |
| 响应消费 ↔ 后端返回 | 前端 interface 字段与后端实际返回一致，无多余 `.data` 解包 | 检查 apiClient 调用是否正确使用解包后的数据 |
| 枚举映射完整 | 后端新增的 enum 值在前端 type/映射/i18n 中全部同步 | grep 后端 enum 定义，对比前端对应文件 |
| TypeScript 编译 | `npx tsc --noEmit` 无报错 | 运行命令检查 |

#### Step 2: 国际化完整性

| 检查项 | 说明 |
|--------|------|
| 无硬编码中文 | 变更文件中的用户可见文案全部走 `useTranslation` |
| i18n key 一致 | `en.ts` 和 `zh.ts` 的 key 集合一致，无遗漏 |
| 动态内容 | 数字/日期/枚举的格式化使用 i18n 工具函数 |

验证方式：
```bash
# 检查变更文件中的硬编码中文（排除注释和 i18n 文件）
git diff main --name-only -- 'frontend/src/**/*.tsx' | xargs grep -n '[\u4e00-\u9fff]' | grep -v '\.ts:.*\/\/' | grep -v 'locales/'
```

#### Step 3: 设计系统合规

| 检查项 | 说明 |
|--------|------|
| 颜色来源 | 无自定义颜色值（`#xxx`/`rgb`），全部来自 Tailwind/Lark token |
| 组件复用 | 无自造的 Button/Modal/Table/Form，使用 shadcn/ui 组件 |
| 布局规范 | 页面标题栏 `bg-white + border-b + h-12`，主按钮 ≤ 2 个 |
| 响应式 | 关键页面在 1280px 和 1440px 下无水平溢出 |
| 空态/加载/错误 | 每个数据展示区域有三态处理，不只有 happy path |

#### Step 4: 组件与架构

| 检查项 | 说明 |
|--------|------|
| 组件职责 | 页面组件不直接写业务逻辑，API 调用经 hooks/services |
| 无副作用泄漏 | `useEffect` 有正确的依赖数组和清理函数 |
| Key 合理 | 列表渲染使用稳定唯一 key（非 index），避免重渲染 bug |
| 路由保护 | 需要权限的页面有权限检查，无权限时重定向或显示提示 |
| 导航配置 | 新页面已在 `navigation.ts` 中注册，权限码正确 |

#### Step 5: 性能嗅探

| 检查项 | 说明 |
|--------|------|
| 不必要的重渲染 | 大型列表/表格组件是否使用 `React.memo` 或 `useMemo` |
| 大型依赖 | 新引入的 npm 包是否影响 bundle size？是否可按需导入？ |
| 图片优化 | 图片是否使用 `next/image`？是否有未压缩的大图？ |
| 无限请求 | `useEffect` + API 调用是否有死循环风险？依赖数组是否正确？ |

#### Step 6: 可访问性基线

| 检查项 | 说明 |
|--------|------|
| 语义 HTML | 使用 `<button>` 而非 `<div onClick>`，`<table>` 而非 `<div>` 布局 |
| 表单标签 | 每个输入框有关联的 `<label>` 或 `aria-label` |
| 焦点管理 | Modal 打开时焦点进入，关闭时焦点回到触发元素 |
| 键盘可用 | 核心交互（提交/取消/导航）可通过键盘完成 |

#### Step 7: 输出自检报告

```
## 前端自检报告

### 契约一致: ✅/⚠️ (N 项通过 / M 项需修复)
### 国际化: ✅/⚠️
### 设计系统: ✅/⚠️
### 组件架构: ✅/⚠️
### 性能嗅探: ✅/⚠️
### 可访问性: ✅/⚠️

### 需修复项:
- [文件:行号] 问题 → 修复方式

### 结论: ready-to-PR / needs-fix
```

发现 `needs-fix` 项时，**就地修复**后重新自检，直到全部通过。

---

## 文档变更后代码对齐检查

当模块文档（01-10）发生大规模变更后，用以下清单检查前端代码是否需要适配。

### Step 1: API 类型对齐（07-api vs services/api/*.ts）

| 检查项 | 方法 |
|--------|------|
| 已删除接口 | 对比 07 接口清单 vs 前端 API service 文件，删除已废弃的 API 函数 |
| 请求参数增删 | 对比 07 请求体 vs 前端 Params interface，字段名必须完全一致 |
| 响应类型增删 | 对比 07 响应体 vs 前端 Response interface，特别关注 nullable 变化 |
| 枚举值同步 | 对比 07/06 枚举 vs 前端 type 定义，已删除的枚举值不应在前端残留 |

### Step 2: 页面路由对齐（05-ui-spec vs app/ 页面）

| 检查项 | 方法 |
|--------|------|
| 已删除页面 | 对比 05 页面清单 vs app/ 目录，已删除的页面文件应移除 |
| 导航配置 | 对比 05 vs navigation.ts，菜单项与页面一致 |
| 权限码 | 对比 01 权限矩阵 vs navigation.ts 中的 permission 字段 |

### Step 3: 国际化同步

| 检查项 | 方法 |
|--------|------|
| 已删除功能文案 | 已删除功能的 i18n key 应从 en.ts/zh.ts 中移除 |
| 新增文案 | 新增的页面/操作/状态的文案是否已补齐 |
| 术语统一 | 文档中统一的术语（如"经理"替代"主管"）是否在前端文案中同步 |

### Step 4: 状态展示对齐（04-state-machine vs 前端状态映射）

| 检查项 | 方法 |
|--------|------|
| 状态枚举 | 已删除的状态值（如 RELEASED）不应在前端 status 映射中残留 |
| 状态流转 UI | 按钮显隐条件与 04 状态机的合法流转一致 |
| 状态标签/颜色 | 新增状态有对应的标签和颜色定义 |

---

## 检查清单

- 页面与交互覆盖 UI 规范关键路径
- 所有文案 i18n，无硬编码（原型模式除外）
- 变更附最小可验证步骤
- 视觉遵循 Lark 设计系统
- **Self-Review 已通过**：契约/i18n/设计系统/架构/性能/可访问性六项自检无 needs-fix 项
- 原型模式额外检查：完全不依赖后端服务

## 参考

- 视觉设计原则：`references/design-principles.md`
- UI 组件细则：`references/ui-guide.md`
- 国际化细则：`references/i18n-guide.md`
- `./references/frontend-standards.md`
- `docs/standards/03-frontend-architecture.md`
