5.8 KiB
5.8 KiB
03 — 前端规则
适用:所有前端 AI 开发任务(Web / H5 / 小程序)。
样式铁律
禁止在组件文件中硬编码:颜色值(#xxx / rgb())、字号(px / rpx / rem)、间距(margin / padding 数值)、圆角(border-radius 数值)。
所有样式必须来自:
- Design Token 文件 → 变量(
$color-primary) - 全局 CSS 类 → 语义类(
.text-h1.card.gap-2) - UI 框架内置类 → Tailwind / Ant Design 等
<!-- ❌ 禁止 -->
<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 有历史 |
| 禁止 | ❌ 无意义分隔注释 | // ========== 数据获取 ========== |
<!--
任务详情页面
- 展示任务基本信息、报名列表、结算记录
- 支持接单、提交、结算等操作
-->
<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。
// ✅ 正确:走项目统一封装
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' })
新增页面模板(填空式)
<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/移动端均正常(如项目需响应式)
- 无禁止清单中的违规项