- 新增 AmountEditor 组件,页面只接一个组件即可获得完整金额编辑体验 - Numpad 增强:支持 cursorIndex 光标输入 + 长按连续删除 - 金额编辑纯函数 amount-edit.ts 集中管理插入/删除/校验规则 - 记一笔、预算页、周期账单弹窗统一接入 AmountEditor - 默认聚焦金额、点击备注/日期/分类隐藏 Numpad、再次点击金额恢复 - 空值输入小数点规范为 0.
128 lines
4.3 KiB
Markdown
128 lines
4.3 KiB
Markdown
## 通用原则
|
||
|
||
- 优先保持现有代码风格与架构模式,不要无理由重构。
|
||
- 任何功能或修复变更,需确保现有测试通过,必要时补充新测试。
|
||
- 所有输出(代码、注释、文档、提交信息)默认使用**简体中文**,专有名词或技术术语可保留英文。
|
||
|
||
## 编码规范
|
||
|
||
### 文件与编码
|
||
- 所有文件必须保持 UTF-8编码。
|
||
- 修改包含中文的文件时,不得造成中文注释、中文文案、错误提示乱码。
|
||
- 如果发现文件已有乱码,不要擅自猜测业务含义,应标记出来让人工确认。
|
||
|
||
## 测试要求
|
||
|
||
- 新增功能必须编写对应的单元测试或集成测试。
|
||
- 修复 Bug 时,先添加能复现问题的测试用例,再修复。
|
||
- 测试覆盖率不应低于当前项目基线(若未设置,尽量保持 ≥80%)。
|
||
- 测试命名清晰,能描述场景,如 should return error when user not found。
|
||
|
||
## 注释与文档
|
||
|
||
### 注释要求
|
||
- **保留**已有的重要注释(如文件头说明、JSDoc、业务解释)。
|
||
- 新增复杂逻辑、公共 API、关键算法时,必须补充**简洁的中文注释**,解释“为什么这么做”而非“做了什么”。
|
||
- 不要为了注释而注释,避免冗余的废话(例如 `i++; // 自增 i`)。
|
||
- 多行 JSDoc/文档注释保持可读性,**不要压缩成单行**。
|
||
|
||
## 小程序适配要点
|
||
|
||
- 页面顶部右侧有元素时,使用 `capsuleRight` 避开胶囊按钮
|
||
- 新页面需要加载数据时,在 `onMounted` 中先 `await waitForReady()` 再请求
|
||
- API 地址统一在 `config.ts` 中配置,不要在各文件中硬编码
|
||
- 头像等用户文件 URL 只存文件名,前端拼接 `API_BASE + 路径 + 文件名`
|
||
|
||
## API 调用规范
|
||
|
||
### request 函数调用格式
|
||
|
||
`request` 函数**只接受一个对象参数**,格式为 `{ url, method?, data? }`。
|
||
|
||
```typescript
|
||
// ✅ 正确
|
||
request({ url: '/feedback', data: params })
|
||
request({ url: '/config' })
|
||
request({ url: '/feedback', method: 'POST', data })
|
||
|
||
// ❌ 错误 — 会导致 URL 拼接为 /apiundefined
|
||
request('/feedback', { params })
|
||
request('/config', { method: 'PUT', data })
|
||
```
|
||
|
||
**原因**:`request` 函数签名是 `request<T>(options: RequestOptions): Promise<T>`,第一个参数是 options 对象,不支持分开传 url 和 options。
|
||
|
||
### 后端 API 响应格式
|
||
|
||
**所有后端路由必须返回统一格式**:
|
||
|
||
```typescript
|
||
// 成功
|
||
res.json({ code: 0, data: ... })
|
||
|
||
// 失败
|
||
res.status(400).json({ code: 40001, message: '错误信息' })
|
||
res.status(403).json({ code: 40300, message: '无权限' })
|
||
res.status(500).json({ code: 50000, message: '服务器错误' })
|
||
```
|
||
|
||
**错误码规范**:
|
||
- `0` — 成功
|
||
- `40001` — 参数错误
|
||
- `40300` — 权限不足
|
||
- `40400` — 资源不存在
|
||
- `50000` — 服务器错误
|
||
|
||
**原因**:前端 `request` 函数检查 `data.code === 0` 判断成功,不返回标准格式会导致前端误判为失败。
|
||
|
||
### 埋点和日志
|
||
|
||
**后端请求日志**:所有请求自动记录到 `logs/YYYY-MM-DD.log`,包含请求方法、URL、状态码、耗时、IP、用户ID。
|
||
|
||
**前端埋点**:使用 `@/utils/tracker` 记录关键操作:
|
||
```typescript
|
||
import { trackAction, trackError, trackApiError } from '@/utils/tracker'
|
||
|
||
trackAction('save_transaction', { amount: 100 }) // 用户操作
|
||
trackError(new Error('xxx'), { context: 'xxx' }) // 错误上报
|
||
```
|
||
|
||
### 页面数据加载模式
|
||
|
||
```typescript
|
||
const initialLoaded = ref(false)
|
||
|
||
onMounted(async () => {
|
||
await waitForReady()
|
||
await loadData()
|
||
initialLoaded.value = true
|
||
})
|
||
|
||
// onShow 时静默刷新,但必须有 initialLoaded 守卫
|
||
onShow(() => {
|
||
if (initialLoaded.value) loadData(true)
|
||
})
|
||
|
||
onPullDownRefresh(async () => {
|
||
await loadData()
|
||
uni.stopPullDownRefresh()
|
||
})
|
||
```
|
||
|
||
## Never 规则
|
||
|
||
- Never 修改 dist/ 目录
|
||
- Never 使用内联样式,除非需要动态计算(胶囊 padding 等除外)
|
||
- Never 在渲染路径中执行耗时操作
|
||
- Never 在列表渲染中省略 key 属性
|
||
- Never 修改 lock 文件
|
||
- Never 使用 git stash
|
||
- Never 切换分支
|
||
- Never 过度封装,保持代码简洁明了
|
||
- Never 未确定需求就开始实现
|
||
|
||
## Requirements
|
||
|
||
- 代码提交前必须检查代码,包括但不限于代码格式、注释、测试覆盖率、文档更新等。
|
||
- 影响到历史数据吧,必须要准备好数据迁移方案。
|