# 变更提案: admin-frontend-plan-toggle-regression

## 元信息
```yaml
类型: 缺陷修复
方案类型: implementation
优先级: P1
状态: 已完成
创建: 2026-04-24
```

---

## 1. 需求

### 背景
用户反馈本地管理端 `#/subscriptions/plans` 页面一加载，就会把所有套餐的“新购”状态取消，属于高风险运营回归：仅浏览页面就会触发真实写操作。

### 目标
- 阻止套餐管理页在加载阶段触发任何 `show / sell / renew` 写操作。
- 保持页面现有 Apple 风格视觉与交互不变，只修复状态开关的数据联动问题。

### 约束条件
```yaml
范围约束: 仅修复套餐管理页加载即误触状态更新的问题，不扩展套餐管理其他功能
技术约束: 继续使用 Vue3 + TypeScript + Element Plus 现有栈，不新增依赖
业务约束: 不改动套餐保存/排序流程，只修补状态开关的数据初始化与提交防护
```

### 验收标准
- [ ] 打开 `#/subscriptions/plans` 时，不会因为初始渲染自动调用状态更新接口。
- [ ] “显示 / 新购 / 续费”开关仅在用户主动切换时才提交更新。
- [ ] `admin-frontend` 构建通过。

---

## 2. 方案

### 根因假设
`ElSwitch` 在收到非 `true/false` 的 `modelValue` 时会立即回退到 `inactiveValue` 并发出 `change`。套餐列表里的 `sell` 字段存在后端返回数值 `0/1` 的情况，导致页面初始渲染时把“新购”批量提交为 `false`。

### 技术方案
1. 在套餐工具层新增开关字段归一化逻辑，把 `show / sell / renew` 统一转换为布尔值后再渲染。
2. 在套餐管理页加载数据时先归一化，再绑定到 `ElSwitch`。
3. 在开关提交逻辑增加“值未变化直接返回”的防抖护栏，防止初始化阶段或重复事件造成误写。

### 风险评估
| 风险 | 等级 | 应对 |
|------|------|------|
| 后端其他接口仍可能返回 `0/1` | 中 | 本次在前端入口先做归一化，确保当前页面稳定 |
| 开关事件护栏过严导致用户点击无效 | 低 | 仅在“新值与当前值完全相同”时跳过，正常点击仍会提交 |

---

## 3. 技术决策

### admin-frontend-plan-toggle-regression#D001: 先在前端入口归一化套餐开关值
**日期**: 2026-04-24
**状态**: ✅采纳
**背景**: 用户选择优先止住页面加载误触写操作。
**决策**: 先在 `admin-frontend` 的套餐列表加载阶段统一把 `show / sell / renew` 转成布尔值，再交给 `ElSwitch`。
**理由**: 能最小代价阻断当前回归，不扩大后端改动面。

### admin-frontend-plan-toggle-regression#D002: 开关提交增加同值短路护栏
**日期**: 2026-04-24
**状态**: ✅采纳
**背景**: 即使后续仍有组件级重复 `change` 事件，也不应再次落库。
**决策**: `handleToggle` 中先比较当前值与目标值，相同则直接返回。
**理由**: 用最小逻辑补一层行为安全网，避免“浏览即写入”的同类回归。
