# AGENTS.md - AI Agent 开发规范

## 项目概述

InkReach 官网项目，基于 Nuxt 4 + Vue 3 + Tailwind CSS v4 的静态展示站点。

## 开发流程

- 包管理器：`npm`
- TypeScript strict 模式，所有代码必须通过类型检查
- 页面与组件代码放在 `app/` 目录下，符合 Nuxt 约定

### 1. 梳理需求

开发前确定好需求类型，在对应的 `plans/` 子目录下创建计划文档：

| 需求类型 | 计划目录          | 命名规则              | 示例                    |
| -------- | ----------------- | --------------------- | ----------------------- |
| 新功能   | `plans/feature/`  | `xxx-xxx-feature.md`  | `hero-banner-feature.md` |
| 修 Bug   | `plans/fix/`      | `xxx-xxx-fix.md`      | `layout-overflow-fix.md` |
| 重构     | `plans/refactor/` | `xxx-xxx-refactor.md` | `component-split-refactor.md` |

计划文档最末尾是开发任务表，每个任务项都有一个对应的二级标题的任务详情描述；当需求完成时任务项也标记勾选了。

### 2. 建立开发分支

使用 `enterprise-git-spec` 技能来建立分支。分支命名：

- 新功能：`feature/xxx-xxx`
- 修 Bug：`bugfix/xxx-xxx`
- 重构：`refactor/xxx-xxx`

### 3. 按照需求开发

按照计划文档的任务项逐步实现功能。

**重要原则：所有测试必须通过**

- 开发过程中，确保所有测试用例通过
- 如果发现现有测试失败，必须立即修复
- 只有在所有测试都通过的情况下，才能认为开发完成

### 4. 合并到开发分支

所有测试通过后，使用 `enterprise-git-spec` 技能提交分支，然后合并回 `develop` 分支。

### 5. 沉淀开发经验

实现功能过程中，将通用的编码好想法、好思想、注意点等及时追加到本文件中。

### 6. 更新项目结构文档

每次功能开发完成后，更新 `docs/references/structs.md` 文件，记录项目目录结构和模块功能描述。

### 7. 更新使用文档

1. `README.md` - 快速上手，新功能开发完成后更新
2. `docs/references/*` - 详细使用指南，每次功能变更后更新
3. `skills/inkreach-official-website/SKILL.md` - AI Agent 使用文档

## 技术栈

| 技术            | 版本  | 用途           |
| --------------- | ----- | -------------- |
| Nuxt            | 4.x   | SSR/SSG 框架   |
| Vue             | 3.x   | UI 框架        |
| Tailwind CSS    | 4.x   | 原子化 CSS     |
| @nuxtjs/seo     | 5.x   | SEO 模块       |
| FontAwesome     | 7.x   | 图标库         |

## 开发规范

### Vue / Nuxt 规范

- 使用 `<script setup>` + Composition API + TypeScript
- 组件文件命名：PascalCase（如 `ProductCard.vue`）
- 页面放在 `app/pages/`，组件放在 `app/components/`
- 布局放在 `app/layouts/`
- 使用 Nuxt 自动导入，无需手动 import 组件和 composables

### 代码风格

- 缩进：2 空格
- 分号：必须
- 引号：单引号
- 行宽：100 字符
- 末尾逗号：es5

### TypeScript 规范

- 所有函数必须声明参数和返回值类型
- 优先使用 `interface`，交叉类型时用 `type`
- 避免使用 `any`，必须使用时用 `unknown` 替代

### Tailwind CSS 规范

- 自定义主题颜色在 `app/assets/css/tailwind.css` 的 `@theme` 中定义
- 自定义动画也在 `@theme` 中通过 `--animate-*` 和 `@keyframes` 注册
- 优先使用 Tailwind 工具类，必要时使用 `@apply` 或 `[任意值]` 语法

### 现有主题色

| 变量名                      | 色值     | 用途         |
| --------------------------- | -------- | ------------ |
| `--color-primary`           | `#fd6a03` | 主色调       |
| `--color-inkreach-homepage-1` | `#f3f4f5` | 浅灰背景     |
| `--color-inkreach-homepage-2` | `#f8f9fb` | 次浅灰背景   |
| `--color-inkreach-homepage-3` | `#fff6f0` | 暖色浅背景   |
| `--color-inkreach-homepage-4` | `#FF6800` | 橙色强调     |
| `--color-inkreach-homepage-5` | `#0C0501` | 深色/页脚    |

### 常用命令

```bash
npm run dev        # 开发模式（nuxt dev --host）
npm run build      # 构建（nuxt build）
npm run generate   # 静态站点生成（nuxt generate）
npm run preview    # 预览构建产物
```

## 经验沉淀

### 启用 Nuxt 文件式路由

项目原先用 `app/app.vue` 作为整页入口。要新增独立路由页（如 `/product-center`）：

1. 把 `app/app.vue` 现有内容迁移到 `app/pages/index.vue`（保持 `/` 渲染原首页）。
2. 将 `app/app.vue` 替换为最小容器：

   ```vue
   <template>
     <NuxtPage />
   </template>
   ```

3. 新建 `app/pages/<name>.vue` 即可通过 `/<name>` 访问。

### Nitro 后端代理模式

若要把外部 HTTP API（如 NestJS 后端）包装为同源 `/api/*` 路由：

```typescript
// server/api/backend/categories.get.ts
export default defineCachedEventHandler(
  async (event) => {
    const config = useRuntimeConfig();
    return $fetch(`${config.public.backendUrl}/public/categories`);
  },
  {
    maxAge: 60,
    swr: true,
    name: 'backend-categories',
    getKey: () => 'tree',
  },
);
```

`runtimeConfig.public.backendUrl` 通过 `.env` 的 `NUXT_PUBLIC_BACKEND_URL` 注入，可被前端直接 `$fetch('/api/backend/...')` 调用且保持 SSR 工作。

注意：带 query 的列表接口（如 goods）需在 `getKey` 中将 `getQuery(event)` JSON 序列化作为缓存键，避免不同筛选条件互相污染。

### 后端响应包络

NestJS 全局 `TransformInterceptor` 把所有响应包成 `{ data: T, success: true }`。前端 `$fetch` 必须读取 `response.data`，类型定义也要带 `BackendEnvelope<T>` 包装层。

### `defineModel` 用于组件 v-model

Nuxt 4 内置 `defineModel` 支持双向绑定，比手写 `modelValue`/`update:modelValue` props+emit 更简洁：

```typescript
const keyword = defineModel<string>('keyword', { default: '' });
const emit = defineEmits<{ search: [] }>();
```

父组件用法：`<ProductFilterBar v-model:keyword="searchKeyword" @search="onSearch" />`

### SSR 期间 watch query 触发请求

在 `<script setup>` 中用 `await loadFilters()` 等顶层 await 时，Nuxt 会等待 Promise 解析才渲染首屏 HTML；之后用 `watch(() => [query.countryId, ...], fetchProducts)` 监听筛选条件变化，重新拉取数据。Top-level await 在 Nuxt 中是合法的（会变为异步 setup）。

### `npm install` 与 `node_modules`

首次 clone 项目后必须 `npm install` 安装依赖（项目使用 `package-lock.json`），否则 `node_modules` 为空。`npm install` 会自动触发 `postinstall: nuxt prepare` 生成 `.nuxt/` 类型与组件清单。

### 类型检查

项目使用 Nuxt 4 默认的 TS 配置（`.nuxt/tsconfig.app.json` 等）。运行类型检查：

```bash
npx vue-tsc --noEmit -p ./.nuxt/tsconfig.app.json
```

退出码 0 表示无类型错误，可作为合并前的本地验证步骤。
