# 03 — 前端规则

> **适用**：所有前端 AI 开发任务（Web / H5 / 小程序）。

---

## 样式铁律

**禁止在组件文件中硬编码**：颜色值（`#xxx` / `rgb()`）、字号（`px` / `rpx` / `rem`）、间距（`margin` / `padding` 数值）、圆角（`border-radius` 数值）。

所有样式必须来自：
1. **Design Token 文件** → 变量（`$color-primary`）
2. **全局 CSS 类** → 语义类（`.text-h1` `.card` `.gap-2`）
3. **UI 框架内置类** → Tailwind / Ant Design 等

```vue
<!-- ❌ 禁止 -->
<style scoped>
.title { color: #333; font-size: 32rpx; margin: 20rpx; }

<!-- ✅ 正确 -->
<style scoped>
.title { color: $color-gray-800; @extend .text-h2; margin: $space-3; }
```

---

## 命名规则

| 元素 | 规则 | 示例（正确 → 错误） |
|------|------|---------------------|
| **组件文件** | 短横线分隔（kebab-case） | `task-card.vue` → ❌ `TaskCard.vue` |
| **页面文件** | 小写或短横线 | `task-detail.vue` `index.vue` |
| **组件 name** | 大驼峰（PascalCase） | `TaskCard` `UserAvatar` |
| **CSS 类名** | 短横线分隔 | `.task-card` `.btn-primary` → ❌ `.taskCard` |
| **JS 变量** | 小驼峰 | `taskList` `userName` → ❌ `task_list` |
| **JS 常量** | 全大写，下划线分隔 | `MAX_PAGE_SIZE` `API_BASE_URL` |
| **JS 函数** | 小驼峰，动词开头 | `fetchTasks()` `handleSubmit()` |
| **API 方法** | 小驼峰，动词+名词 | `taskApi.list()` `taskApi.create()` |
| **路由路径** | 小写，短横线 | `/task-detail` → ❌ `/taskDetail` |
| **文件目录** | 小写或短横线 | `task-hall/` `user-center/` |
| **静态资源** | 短横线分隔 | `icon-phone.svg` `bg-home.jpg` |

---

## 注释规则

| 位置 | 要求 | 示例 |
|------|------|------|
| **组件** | 顶部注释说明用途和 Props | `<!-- 任务卡片组件，用于列表展示 -->` |
| **复杂逻辑** | 行内注释说明"为什么" | `// 先查本地缓存，未命中再请求接口` |
| **计算属性** | 注释说明计算依据 | `// 根据任务状态和截止时间计算紧急程度` |
| **API 方法** | JSDoc 说明入参/返回 | `@param {Object} params @returns {Promise<{list,total}>}` |
| **TODO/FIXME** | 负责人+日期 | `// TODO(yaoshuli 2026-06) 待产品确认逻辑` |
| **禁止** | ❌ 注释写"做了什么" | `<!-- 循环渲染列表 -->` ← 删掉 |
| **禁止** | ❌ 注释掉的旧代码/旧模板 | 直接删，Git 有历史 |
| **禁止** | ❌ 无意义分隔注释 | `// ========== 数据获取 ==========` |

```vue
<!--
  任务详情页面
  - 展示任务基本信息、报名列表、结算记录
  - 支持接单、提交、结算等操作
-->
<template>
  <app-layout>
    <!-- 任务信息卡片 -->
    <m-card>
      ...
    </m-card>

    <!-- 仅管理员可见 -->
    <m-card v-if="isAdmin">
      ...
    </m-card>
  </app-layout>
</template>

<script setup>
/**
 * 提交任务成果。
 * 提交成功后自动刷新列表并发送通知。
 *
 * @param {string} taskId - 任务ID
 * @param {Object} payload - 提交内容 { files, remark }
 * @returns {Promise<void>}
 */
const handleSubmit = async (taskId, payload) => {
  // 先本地校验，避免无效请求
  if (!payload.files.length) return;

  await taskApi.submit(taskId, payload);
  // TODO(yaoshuli 2026-06) 成功后应跳转到提交记录页
  router.push('/task-list');
};
</script>
```

---

## 组件使用铁律

| 场景 | ✅ 必须用 | ❌ 禁止 |
|------|----------|---------|
| 页面外壳 | 项目统一布局组件 | 自己写页头/侧栏/底栏/TabBar |
| 图标 | 项目统一图标组件 | 裸 `<img>` / emoji / Unicode |
| 按钮 | 项目统一按钮组件 | 自己写 button 样式 |
| 卡片 | 项目统一卡片组件 | 裸露 div+手写圆角阴影 |

---

## API 调用铁律

**所有 HTTP 请求走统一封装**，禁止组件内直调原生 API。

```javascript
// ✅ 正确：走项目统一封装
import { taskApi } from '@/services/task';
const res = await taskApi.list({ page: 1, size: 20 });

// ❌ 禁止
fetch('/api/task/list').then(...)
axios.get('/api/task/list')
uni.request({ url: '/api/task/list' })
```

---

## 新增页面模板（填空式）

```vue
<template>
  <app-layout>
    <!-- [填空] 页面内容 -->
    <view v-if="loading">加载中...</view>
    <view v-else-if="list.length === 0" class="empty-state">
      <text>暂无数据</text>
    </view>
    <view v-else>
      <!-- [填空] 数据展示 -->
    </view>
  </app-layout>
</template>

<script setup>
import { ref, onMounted } from 'vue';
// [填空] import API 方法

const list = ref([]);
const loading = ref(false);

const fetchData = async () => {
  loading.value = true;
  try {
    const res = await [API方法]({ page: 1, size: 20 });
    list.value = res.list;
  } finally {
    loading.value = false;
  }
};

onMounted(() => fetchData());
</script>

<style scoped lang="scss">
// ⚠️ 不得超过 100 行，超过说明在造轮子
</style>
```

---

## 禁止清单

| 类别 | ❌ 禁止 |
|------|--------|
| **样式** | 硬编码颜色/字号/间距/圆角 |
| **样式** | 在 scoped 中定义全局重置 |
| **样式** | scoped 超过 100 行 |
| **组件** | 自己写布局外壳（页头/侧栏/TabBar） |
| **组件** | 裸用 `<img>` 代替图标组件 |
| **网络** | 直调 `fetch` / `axios` / `uni.request` |
| **网络** | 不处理错误态 |
| **状态** | 无加载态/空态 |

---

## 检查表

每完成一个前端任务，逐项自检：

- [ ] 使用项目统一布局组件
- [ ] 无硬编码颜色/字号/间距/圆角
- [ ] 图标全部走统一图标组件
- [ ] API 调用走统一封装，无裸调
- [ ] 页面/组件有加载态 + 空态 + 错误态
- [ ] 关键操作有 loading 态（防重复点击）
- [ ] `<style scoped>` 不超过 100 行
- [ ] PC/移动端均正常（如项目需响应式）
- [ ] 无禁止清单中的违规项
