---
globs: ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"]
---

# JavaScript 编码规范

## 文件与编码
- 使用无 BOM 的 UTF-8 编码
- 文件结尾保留一个空行

## 缩进与空格
- 使用 4 个空格缩进，禁止使用 2 空格或 Tab
- `switch` 的 `case` 和 `default` 必须增加一个缩进层级
- 二元运算符两侧必须有空格，一元运算符与操作对象之间不允许有空格
- 左花括号 `{` 前必须有空格
- `if/else/for/while/function/switch/do/try/catch/finally` 关键字后必须有空格
- 对象属性 `:` 后必须有空格，`:` 前不允许有空格
- 函数名和 `(` 之间不允许有空格
- `,` 和 `;` 前不允许有空格，后面（非行尾）必须有空格
- `()`、`[]` 内紧贴括号部分不允许有空格
- 行尾不得有多余空格

## 换行
- 每个独立语句结束后必须换行
- 每行不超过 120 个字符
- 运算符处换行时，运算符必须在新行行首
- 禁止在 `,` 或 `;` 前换行

## 语句
- 禁止省略语句结束的分号
- `if/else/for/do/while` 即使只有一行也必须使用 `{}`
- 函数定义结束不加分号，函数表达式结束必须加分号
- IIFE 必须在函数表达式外添加 `(`

## 命名
- 变量：camelCase，如 `loadingModules`
- 常量：全大写下划线分隔，如 `HTML_ENTITY`
- 函数：camelCase，使用动宾短语，如 `getStyle`
- 函数参数：camelCase
- 类：PascalCase，使用名词，如 `TextNode`
- 类的方法/属性：camelCase
- 枚举变量：PascalCase；枚举属性：全大写下划线分隔
- 命名空间：camelCase
- boolean 变量使用 `is` 或 `has` 开头，如 `isReady`
- Promise 对象用动宾短语进行时，如 `loadingData`
- 多单词缩写词大小写与首字母保持一致
- 禁止使用中文作为变量名或属性名

## 变量声明
- 变量和函数必须先定义后使用，禁止隐式全局变量
- 每个 `var/let/const` 只声明一个变量
- 变量即用即声明，禁止在函数顶部统一声明所有变量

## 条件判断
- 使用严格等于 `===`，仅判断 null/undefined 时允许 `== null`
- 使用简洁表达式：空字符串 `!name`、非空 `name`、数组非空 `collection.length`
- 按执行频率排列分支顺序
- 同一变量多值条件使用 `switch` 替代 `if-else`
- else 块后无语句时删除 else

## 循环
- 循环体中禁止包含函数表达式，提取到循环体外
- 循环内多次使用的不变值在循环外缓存
- 遍历有序集合时缓存 `length`
- 遍历对象属性时使用 `hasOwnProperty` 过滤

## 类型使用
- 字符串使用单引号 `'`
- 字符串拼接优先使用模板字符串（ES6+）或数组 `join`
- 数值转换使用 `+str`（转 number）、`String(num)`（转 string），禁止 `new Number/String/Boolean`
- 转 boolean 使用 `!!`
- parseInt 必须指定进制参数：`parseInt(str, 10)`
- 浮点数运算注意精度问题

## 对象与数组
- 对象创建使用字面量 `{}`，禁止 `new Object()`
- 数组创建使用字面量 `[]`，禁止 `new Array()`
- 对象属性使用 `.` 访问，变量属性使用 `[]` 访问
- for-in 遍历对象时必须使用 `hasOwnProperty` 过滤
- 禁止使用 `delete` 操作数组元素（产生空洞），使用 `splice`

## 函数
- 函数长度不超过 50 行
- 函数参数不超过 6 个，超过时使用对象封装
- 禁止修改函数参数值（引用类型注意副作用）
- 闭包中注意内存泄漏和 this 指向问题

## 注释
- 单行注释独占一行，`//` 后跟一个空格
- 文件顶部必须包含 `@file` 文件注释
- 函数注释必须包含说明、`@param` 和 `@return`（含类型信息）
- 基本类型 `{string}/{number}/{boolean}` 首字母小写
- 特殊标记：`TODO`（待实现）、`FIXME`（需修正）、`HACK`（诡异手段）、`XXX`（陷阱）

## DOM 操作
- 操作 DOM 时缓存 DOM 引用，避免重复查询
- 批量 DOM 修改使用 DocumentFragment 或一次性 innerHTML
- 事件绑定优先使用事件委托
- 移除 DOM 元素前先移除事件监听

## 异步
- 回调嵌套不超过 3 层，使用 Promise 或 async/await
- Promise 必须处理 reject：添加 `.catch()` 或 try-catch
- async 函数中 await 必须在 try-catch 中使用

## 接口状态码常量（禁止硬编码状态码字符串）

所有接口响应状态码判断必须使用 `STATUSCODE` 常量，禁止硬编码 `'M0200'`、`'M0301'` 等字符串。

```javascript
import { STATUSCODE } from '../../../../assets/js/defined';

// ❌ 禁止
if (res.data && res.data.status === 'M0200') { ... }

// ✅ 正确（使用 == 与项目现有代码保持一致）
if (res.data.status == STATUSCODE.code01) { ... }
```

| 常量 | 值 | 含义 |
|------|-----|------|
| `STATUSCODE.code01` | `M0200` | 操作成功 |
| `STATUSCODE.code02` | `M0300` | 用户数据有误 |
| `STATUSCODE.code03` | `M0500` | 服务端异常 |
| `STATUSCODE.code04` | `M0201` | 成功但不执行标准逻辑 |
| `STATUSCODE.code08` | `M0301` | 业务校验不通过 |
| `STATUSCODE.code09` | `M0302` | 数据状态冲突 |

## 金额格式化（禁止手写 formatAmount）

禁止在组件中自定义 `formatAmount` 方法，必须使用项目全局工具 `$function.AMOUNT`。

| 方法 | 说明 | 示例 |
|------|------|------|
| `$function.AMOUNT.format(val)` | 2 位小数 + 千分位 | `1,234.56` |
| `$function.AMOUNT.format(val, 0)` | 0 位小数 + 千分位 | `1,234` |
| `$function.AMOUNT.toNumber(str)` | 格式化字符串转回数字 | `1234.56` |
| `$function.AMOUNT.uppercase(val)` | 金额转中文大写 | `壹仟贰佰叁拾肆元伍角陆分` |

来源：`zqyl-module-function` 包，通过 `Vue.prototype.$function` 全局注册。

```vue
<!-- ❌ 禁止：手写格式化 -->
{{ formatAmount(detail.bpAmt) }}

<!-- ✅ 正确：使用全局工具 -->
{{ $function.AMOUNT.format(detail.bpAmt) || '--' }}
```

## 时间格式化（禁止手写日期格式化方法）

禁止自定义 `formatDateTime` / `formatTime` 方法，必须使用项目全局工具 `$function.DATE`。

| 用法 | 输出格式 | 示例 |
|------|---------|------|
| `$function.DATE.strToTime(ts)` | `YYYY-MM-DD` | `2026-03-24` |
| `$function.DATE.strToTime(ts, 'hms')` | `YYYY-MM-DD HH:mm:ss` | `2026-03-24 14:30:45` |
| `$function.DATE.strToTime(ts, 'hm')` | `YYYY-MM-DD HH:mm` | `2026-03-24 14:30` |
| `$function.DATE.strToTime(ts, 'hms', '/')` | `YYYY/MM/DD HH:mm:ss` | `2026/03/24 14:30:45` |
| `$function.DATE.now(separator)` | 当前日期字符串 | — |
| `$function.DATE.timeToStr(dateStr)` | 日期字符串转时间戳 | — |

```vue
<!-- ❌ 禁止：自定义格式化方法 -->
{{ formatDateTime(scope.row.modifyTime) }}

<!-- ✅ 正确：直接在模板中调用 -->
{{ $function.DATE.strToTime(scope.row.modifyTime, 'hms') || '--' }}
```

在 yl-table 中优先使用声明式配置（无需写 slot）：
```javascript
{ prop: 'operateTime', label: '操作时间', width: 160,
  attr: { type: 'date', format: 'yyyy-MM-dd HH:mm:ss' } }
```

## 常见错误模式
- ❌ `if (age == 30)` → ✅ `if (age === 30)`
- ❌ `var a = 1, b = 2;` → ✅ `var a = 1; var b = 2;`
- ❌ `if (condition) callFunc();` → ✅ `if (condition) { callFunc(); }`
- ❌ `parseInt("08")` → ✅ `parseInt("08", 10)`
- ❌ 循环中 `new Function` → ✅ 提取到循环外
- ❌ 硬编码 `=== 'M0200'` → ✅ `== STATUSCODE.code01`
- ❌ 组件内定义 `formatAmount` 方法 → ✅ `$function.AMOUNT.format(val)`
- ❌ 自定义 `formatDateTime` 方法 → ✅ `$function.DATE.strToTime(ts, 'hms')`
