# PRD 需求文档 - 前端适配模块父子层级结构

> **文档类型**: PRD 需求文档
> **创建日期**: 2026-07-22
> **作者**: czj
> **优先级**: P2
> **状态**: 待开发

---

## 一、需求背景

### 1.1 问题描述

后端已完成模块表 `parent_id` 字段的完整实现，支持模块的父子层级结构：

- **模型层**：`Module` 模型新增 `parent_id` 字段和自引用关系（`parent`/`children`）
- **Schema层**：`ModuleCreate`/`ModuleUpdate`/`ModuleResponse` 增加 `parent_id`/`children` 字段
- **Service层**：`create`/`update` 支持 `parent_id` 参数
- **API层**：响应中包含 `parentId` 字段（camelCase 转换）

但前端尚未适配，存在以下问题：

| 问题 | 影响 |
|------|------|
| TypeScript 类型定义缺少 `parentId`/`children` | 类型检查不完整，IDE 提示缺失 |
| 模块选择器是扁平下拉列表 | 无法直观展示模块层级关系 |
| Modules.vue 编辑弹窗不支持父模块选择 | 无法在 UI 层面管理模块层级 |

### 1.2 目标用户

- **测试人员**：需要按模块层级筛选和管理用例
- **管理员**：需要配置和维护模块父子层级关系

### 1.3 预期效果

1. 模块选择器展示层级结构，用户可快速定位模块
2. 编辑模块时可选择父模块，建立层级关系
3. 类型定义完整，代码可维护性提升

---

## 二、功能需求

### 2.1 需求清单

| 功能 | 描述 | 优先级 |
|------|------|--------|
| TypeScript 类型定义 | Module 接口增加 parentId/children 字段 | P0 |
| 模块选择器树形化 | Cases.vue、Recorder.vue 使用 el-tree-select | P0 |
| 编辑弹窗父模块选择 | Modules.vue 编辑弹窗支持选择父模块 | P0 |
| 循环引用防护 | 编辑时排除当前模块及其子模块 | P1 |

### 2.2 模块选择器树形化

**涉及页面**：

| 页面 | 位置 | 说明 |
|------|------|------|
| Cases.vue | 筛选区模块下拉 | 筛选用例列表 |
| Cases.vue | 编辑弹窗模块选择 | 新建/编辑用例时选择所属模块 |
| Recorder.vue | 模块选择下拉 | 录制用例时选择所属模块 |

**UI 规范**：

- 使用 Element Plus `el-tree-select` 组件
- props 配置：`{ label: 'name', value: 'id', children: 'children' }`
- `check-strictly`：允许选择任意层级（父级/子级均可）
- `clearable`：支持清空选择
- 树形展开，层级缩进显示

### 2.3 编辑弹窗父模块选择

**Modules.vue 编辑弹窗新增字段**：

| 字段 | 类型 | 说明 |
|------|------|------|
| 父模块 | el-tree-select | 可选，选择父模块建立层级关系 |

**循环引用防护**：

- 编辑模块时，父模块选择器需排除：
  1. 当前模块自身
  2. 当前模块的所有子模块（递归）

防止形成循环引用：A → B → A 或 A → B → C → A

### 2.4 TypeScript 类型定义

**Module 接口扩展**：

```typescript
export interface Module {
  id: string
  name: string
  description: string
  icon: string
  order: number
  moduleType?: string
  parentId?: string | null      // 新增：父模块ID
  children?: Module[]           // 新增：子模块列表
  caseCount?: number
  passRate?: number
  createdAt: string
  updatedAt: string
}
```

**CreateModuleRequest / UpdateModuleRequest 扩展**：

```typescript
parent_id?: string | null  // 新增：父模块ID
```

---

## 三、技术方案

### 3.1 数据流

```
后端 API (/api/modules)
    ↓ 返回扁平列表（含 parentId）
前端 API 层 (buildTree 函数)
    ↓ 构建树形结构
页面组件 (el-tree-select)
    ↓ 展示层级
```

### 3.2 树形数据构建

```typescript
/**
 * 将扁平模块列表构建为树形结构
 * @param items 扁平模块列表
 * @returns 树形结构数据
 */
function buildTree(items: Module[]): Module[] {
  const map = new Map<string, Module>()
  const roots: Module[] = []

  // 建立映射
  items.forEach(item => map.set(item.id, { ...item, children: [] }))

  // 构建树
  items.forEach(item => {
    const node = map.get(item.id)!
    if (item.parentId && map.has(item.parentId)) {
      const parent = map.get(item.parentId)!
      if (!parent.children) parent.children = []
      parent.children.push(node)
    } else {
      roots.push(node)
    }
  })

  return roots
}
```

### 3.3 前端文件改动

| 文件 | 改动 |
|------|------|
| `frontend/src/types/module.ts` | 增加 parentId/children 字段 |
| `frontend/src/api/modules.ts` | 新增 buildTree 辅助函数并导出 |
| `frontend/src/views/Cases.vue` | el-select → el-tree-select（2处） |
| `frontend/src/views/Recorder.vue` | el-select → el-tree-select（1处） |
| `frontend/src/views/Modules.vue` | 编辑弹窗增加父模块选择字段 |

---

## 四、界面设计

### 4.1 模块选择器（树形）

```
┌─────────────────────────────────────┐
│ 选择模块                          ▼  │
├─────────────────────────────────────┤
│ ▼ 预定2.0                           │
│     └── 会议管理                    │
│ ▼ 数据统计                          │
│     ├── 数据分析                    │
│     ├── 管理看板                    │
│     └── 运维管理                    │
│ ▼ 集控控制                          │
│     ├── 设备列表                    │
│     └── 远程控制                    │
│     ...                             │
└─────────────────────────────────────┘
```

### 4.2 模块编辑弹窗（新增父模块字段）

```
┌─────────────────────────────────────┐
│ 编辑模块                          ✕  │
├─────────────────────────────────────┤
│ 模块名称: [会议管理            ]    │
│ 模块类型: [标准模块          ▼ ]    │
│ 父模块:   [预定2.0           ▼ ]    │ ← 新增
│ 模块描述: [                        ] │
│           [                        ] │
│ 模块图标: [folder              ]    │
├─────────────────────────────────────┤
│              [取消]  [确定]          │
└─────────────────────────────────────┘
```

---

## 五、验收标准

| 测试项 | 预期结果 |
|--------|----------|
| TypeScript 编译 | `npm run build` 无类型错误 |
| Cases.vue 筛选模块选择器 | 展示树形层级，可筛选 |
| Cases.vue 编辑弹窗模块选择器 | 展示树形层级，可选择任意模块 |
| Recorder.vue 模块选择器 | 展示树形层级 |
| Modules.vue 编辑弹窗父模块选择 | 可选择父模块，保存后层级正确 |
| 循环引用防护 | 编辑时不能选择自己或子模块作为父模块 |
| 无父模块的叶子模块 | 正常显示在选择器中 |
| 清空父模块 | 可清空，模块变为顶级模块 |

---

## 六、相关文档

- `backend/app/models/module.py` - 模块模型（已实现 parent_id）
- `backend/app/schemas/module.py` - 模块 Schema（已实现 parentId/children）
- `frontend/src/types/module.ts` - 前端类型定义（待更新）
- `frontend/src/views/Cases.vue` - 用例管理页面
- `frontend/src/views/Recorder.vue` - 用例录制器页面
- `frontend/src/views/Modules.vue` - 模块管理页面

---

*本文档由 Claude Code 生成，遵循项目 PRD 文档规范。*