# 执行计划 — 预设配置与登录账号预设

> **生成时间**: 2026-08-20
> **基于 PRD**: `_PRD_预设配置与登录账号预设.md`
> **当前分支**: `platform-auto-test`
> **预计工作量**: 8 个文件改动（后端 4 + 前端 4）+ 前端构建验证

---

## 概述

将「接口预设」更名为「预设配置」，并在预设中新增登录凭据（凭据名称/用户名/密码），新建任务选择预设时自动填充登录凭据，用户无需手动输入。

### 核心改动一览

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `backend/app/models/api_preset.py` | 修改 | 新增 `credential_name`/`login_username`/`login_password` 字段 + `to_dict()` 返回 |
| `backend/app/schemas/api_preset.py` | 修改 | Create/Update/Response/ListItem 新增三个字段 |
| `backend/app/database.py` | 修改 | `_ensure_columns` 追加 3 条 ALTER TABLE |
| `backend/app/services/performance_service.py` | 修改 | `create_task()` 预设继承补充 login_username/login_password |
| `frontend/src/App.vue` | 修改 | 侧边栏菜单文字「接口预设」→「预设配置」 |
| `frontend/src/router/index.ts` | 修改 | meta.title「接口预设」→「预设配置」 |
| `frontend/src/views/performance/ApiPresetList.vue` | 修改 | 页面文案 + 新增「凭据名称」列 |
| `frontend/src/views/performance/ApiPresetDialog.vue` | 修改 | 编辑弹窗新增凭据名称/用户名/密码输入框 |
| `frontend/src/views/performance/TaskList.vue` | 修改 | 预设摘要 + onPresetChange 填充登录凭据 |

---

## Phase 1: 后端模型 + Schema（2 文件）

### 1.1 models/api_preset.py

在 `ApiPreset` 类中新增三个字段（放在 `account_key` 字段之后、`capture_rules` 之前）：

```python
# 登录凭据配置
credential_name: Mapped[Optional[str]] = mapped_column(
    String(100), nullable=True, default=None, comment="凭据名称，如'超级管理员'"
)
login_username: Mapped[Optional[str]] = mapped_column(
    String(200), nullable=True, default=None, comment="自定义登录用户名，留空使用默认"
)
login_password: Mapped[Optional[str]] = mapped_column(
    String(200), nullable=True, default=None, comment="自定义登录密码，留空使用默认"
)
```

`to_dict()` 方法中新增返回：

```python
"credential_name": self.credential_name,
"login_username": self.login_username,
"login_password": self.login_password,
```

### 1.2 schemas/api_preset.py

**ApiPresetCreate** — 新增三个可选字段：

```python
credential_name: Optional[str] = Field(None, description="凭据名称，如'超级管理员'")
login_username: Optional[str] = Field(None, description="自定义登录用户名，留空使用默认")
login_password: Optional[str] = Field(None, description="自定义登录密码，留空使用默认")
```

**ApiPresetUpdate** — 同上：

```python
credential_name: Optional[str] = Field(None, description="凭据名称")
login_username: Optional[str] = Field(None, description="自定义登录用户名")
login_password: Optional[str] = Field(None, description="自定义登录密码")
```

**ApiPresetResponse** — 新增返回：

```python
credential_name: Optional[str] = None
login_username: Optional[str] = None
login_password: Optional[str] = None
```

**ApiPresetListItem** — 列表项仅需返回凭据名称（用于下拉和列表展示）：

```python
credential_name: Optional[str] = None
```

### 1.3 数据库迁移

在 `database.py` 的 `_ensure_columns()` 方法中追加：

```python
# 预设配置：登录凭据名称/用户名/密码（旧库升级）
("api_presets", "credential_name", "VARCHAR(100) DEFAULT NULL"),
("api_presets", "login_username", "VARCHAR(200) DEFAULT NULL"),
("api_presets", "login_password", "VARCHAR(200) DEFAULT NULL"),
```

---

## Phase 2: 后端 Service 预设继承补充（1 文件）

### 2.1 services/performance_service.py

在 `create_task()` 方法中，预设继承块（`if data.get("preset_id")`）内，补充 login_username/login_password 的继承逻辑。

**当前代码**（约第 190-240 行）：

```python
if data.get("preset_id"):
    preset = await self.db.get(ApiPreset, data["preset_id"])
    if not preset:
        raise HTTPException(status_code=404, detail="预设不存在")
    # 预设字段作为默认值，任务显式传入的字段覆盖
    if not data.get("method"):
        data["method"] = preset.method
    if not data.get("target_url"):
        data["target_url"] = preset.target_url
    # ... 其他字段 ...
    if not data.get("account_key"):
        data["account_key"] = preset.account_key
    # ⬇️ 新增：从预设继承登录凭据
    if not data.get("login_username"):
        data["login_username"] = preset.login_username
    if not data.get("login_password"):
        data["login_password"] = preset.login_password
```

**注意**：`login_username`/`login_password` 在 `PerformanceTask` 模型中已存在（第 111-114 行），无需再改模型。

---

## Phase 3: 前端文案更新 + 菜单/路由（2 文件）

### 3.1 App.vue

搜索 `接口预设` 字符串，改为 `预设配置`。

```html
<!-- 第 90 行附近 -->
<el-menu-item index="/performance/presets">预设配置</el-menu-item>
```

侧边栏面板的 `presets` 映射也要改：

```typescript
// 第 286 行附近
presets: '预设配置',
```

### 3.2 router/index.ts

```typescript
// 第 188 行附近
meta: { title: '预设配置' }
```

---

## Phase 4: 前端预设配置列表页（1 文件）

### 4.1 ApiPresetList.vue

**文案变更**：
- 提示文案：`接口预设用于快速填充任务配置` → `预设配置用于快速填充任务配置`

**新增列**：在「方法」列之后、「目标 URL」列之前插入凭据名称列：

```html
<el-table-column prop="credentialName" label="凭据名称" width="150" show-overflow-tooltip>
  <template #default="{ row }">
    <span>{{ row.credentialName || '-' }}</span>
  </template>
</el-table-column>
```

---

## Phase 5: 前端预设编辑弹窗（1 文件）

### 5.1 ApiPresetDialog.vue

**弹窗标题**：`新建接口预设` → `新建预设配置`，`编辑接口预设` → `编辑预设配置`

**新增表单字段**：在「账号」select 下方增加凭据配置区域（仅 `authRequired=true` 时显示）：

```html
<!-- 认证配置区域末尾，accountKey 之后 -->
<el-divider v-if="form.authRequired" style="margin: 8px 0" />
<template v-if="form.authRequired">
  <div class="credential-section-label">登录凭据（可选，留空使用默认值）</div>
  <el-row :gutter="16">
    <el-col :span="8">
      <el-form-item label="凭据名称">
        <el-input v-model="form.credentialName" placeholder="如：超级管理员" clearable />
      </el-form-item>
    </el-col>
    <el-col :span="8">
      <el-form-item label="登录用户名">
        <el-input v-model="form.loginUsername" placeholder="留空使用默认" clearable />
      </el-form-item>
    </el-col>
    <el-col :span="8">
      <el-form-item label="登录密码">
        <el-input v-model="form.loginPassword" type="password" show-password placeholder="留空使用默认" clearable />
      </el-form-item>
    </el-col>
  </el-row>
</template>
```

**表单数据模型 (PresetForm)** 新增字段：

```typescript
interface PresetForm {
  // ... 现有字段 ...
  credentialName: string
  loginUsername: string
  loginPassword: string
}
```

**defaultForm()** 新增默认值：

```typescript
const defaultForm = (): PresetForm => ({
  // ... 现有 ...
  credentialName: '',
  loginUsername: '',
  loginPassword: '',
})
```

**initForm()** — 编辑时回填：

```typescript
form.credentialName = p.credentialName || ''
form.loginUsername = p.loginUsername || ''
form.loginPassword = p.loginPassword || ''
```

**handleSave()** — 提交数据补发：

```typescript
const data: any = {
  // ... 现有 ...
  credentialName: form.credentialName || null,
  loginUsername: form.loginUsername || null,
  loginPassword: form.loginPassword || null,
}
```

---

## Phase 6: 前端任务列表预设填充（1 文件）

### 6.1 TaskList.vue

**onPresetChange()** — 选择预设后填充登录凭据：

在 `onPresetChange` 函数的赋值段末尾（`form.accountKey` 之后）补充：

```typescript
// 填充登录凭据
form.loginUsername = preset.loginUsername || ''
form.loginPassword = preset.loginPassword || ''
```

**预设摘要卡片** — 显示凭据名称：

在认证行中，当前显示 `需要登录（超级管理员）` 的逻辑已通过 `accountKeyLabel(preset.accountKey)` 显示。需改为显示预设的 `credentialName`：

```html
<div>
  <strong>认证：</strong>
  {{ selectedPreset.authRequired ? '需要登录' : '无需登录' }}
  <template v-if="selectedPreset.authRequired">
    （{{ selectedPreset.credentialName || accountKeyLabel(selectedPreset.accountKey) }}）
  </template>
   · 签名：{{ selectedPreset.signRequest ? '需要' : '不需要' }}
</div>
```

**注意**：优先显示 `credentialName`，回退显示 `accountKey` 对应的中文标签，保持向后兼容。

---

## 验证清单

### 后端验证

| # | 验证项 | 方式 |
|---|--------|------|
| 1 | 模型新增字段编译通过 | `py_compile` 检查 models/api_preset.py |
| 2 | Schema 新增字段编译通过 | `py_compile` 检查 schemas/api_preset.py |
| 3 | 旧库启动自动加列 | 启动后查表 `api_presets` 确认列存在 |
| 4 | POST /api/performance/presets 带凭据字段 | 201 创建成功，返回含 credential_name/login_username |
| 5 | POST /api/performance/presets 不带凭据字段 | 201 创建成功，字段为 null |
| 6 | POST /api/performance/tasks 带 preset_id 含凭据 | 任务创建，login_username/login_password 从预设继承 |
| 7 | POST /api/performance/tasks 带 preset_id 但传了 login_username | 任务显式值覆盖预设值 |

### 前端验证

| # | 验证项 | 方式 |
|---|--------|------|
| 8 | 侧边栏菜单显示「预设配置」 | 打开页面确认 |
| 9 | 预设配置列表页文案正确 | 标题、提示文案均为「预设配置」 |
| 10 | 列表页显示凭据名称列 | 有预设→显示凭据名；无预设→显示 `-` |
| 11 | 新建预设弹窗有凭据三个输入框 | 打开弹窗，确认输入框显示 |
| 12 | 编辑预设弹窗回填凭据 | 保存后重新打开，数据正确 |
| 13 | 新建任务选择预设 → 自动填充登录凭据 | 表单 loginUsername/loginPassword 自动填入 |
| 14 | 摘要卡片显示凭据名称 | 卡片显示「需要登录（超级管理员）」 |
| 15 | 预设凭据为空 → 摘要显示 account_key 标签 | 回退显示「超级管理员（admin）」 |
| 16 | 前端构建通过 | `npm run build` 无 TS 错误 |

---

## 执行顺序

```
Step 1: models/api_preset.py 新增字段 + to_dict()
Step 2: schemas/api_preset.py 新增字段
Step 3: database.py 追加迁移
Step 4: services/performance_service.py 预设继承补充
Step 5: App.vue + router/index.ts 菜单文案
Step 6: ApiPresetList.vue 文案 + 列
Step 7: ApiPresetDialog.vue 弹窗 + 凭据输入框
Step 8: TaskList.vue 预设填充 + 摘要
Step 9: npm run build 验证
```

---

*本文档由 Claude Code 生成，基于 PRD 文档 `_PRD_预设配置与登录账号预设.md`。*