HaniBlindBox/.kiro/specs/menu-crud/design.md
2026-01-05 00:05:04 +08:00

189 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Design Document: Menu CRUD
## Overview
完善菜单管理页面的 CRUD 功能。后端 API 已完整实现,本设计主要关注前端实现:补充 API 接口定义、实现表单对话框、完善操作逻辑。
## Architecture
```
┌─────────────────────────────────────────────────────────┐
│ Menu Page (index.vue) │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Menu Table │ │ Menu Dialog │ │ Delete Confirm │ │
│ │ (Tree View) │ │ (Form) │ │ Dialog │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ Menu API (menu.ts) │
│ getMenuTree | createMenu | updateMenu | deleteMenu │
├─────────────────────────────────────────────────────────┤
│ Backend API (已实现) │
│ GET /menus | POST /menus | PUT /menus/{id} | DELETE │
└─────────────────────────────────────────────────────────┘
```
## Components and Interfaces
### Menu API 接口补充
```typescript
// 创建菜单请求
export interface CreateMenuRequest {
parentId: number
name: string
path?: string
component?: string
icon?: string
menuType: number // 1=目录, 2=菜单, 3=按钮
permission?: string
sortOrder: number
status: number // 1=显示, 0=隐藏
isExternal: boolean
isCache: boolean
}
// 更新菜单请求
export interface UpdateMenuRequest {
parentId: number
name: string
path?: string
component?: string
icon?: string
menuType: number
permission?: string
sortOrder: number
status: number
isExternal: boolean
isCache: boolean
}
// API 函数
export function createMenu(data: CreateMenuRequest): Promise<ApiResponse<number>>
export function updateMenu(id: number, data: UpdateMenuRequest): Promise<ApiResponse<void>>
export function deleteMenu(id: number): Promise<ApiResponse<void>>
export function getMenuById(id: number): Promise<ApiResponse<MenuTree>>
```
### Menu Form 表单字段
| 字段 | 类型 | 组件 | 验证规则 |
|------|------|------|----------|
| parentId | number | el-tree-select | 可选默认0表示顶级 |
| name | string | el-input | 必填 |
| menuType | number | el-radio-group | 必填1/2/3 |
| icon | string | el-input | 可选,目录/菜单时显示 |
| path | string | el-input | 菜单类型时必填 |
| component | string | el-input | 菜单类型时必填 |
| permission | string | el-input | 按钮类型时必填 |
| sortOrder | number | el-input-number | 必填,>=0 |
| status | number | el-radio-group | 必填0/1 |
| isExternal | boolean | el-switch | 可选 |
| isCache | boolean | el-switch | 可选 |
### 表单验证逻辑
```typescript
const formRules = {
name: [{ required: true, message: '请输入菜单名称' }],
menuType: [{ required: true, message: '请选择菜单类型' }],
path: [{
required: true,
validator: (rule, value, callback) => {
if (formData.menuType === 2 && !value) {
callback(new Error('菜单类型必须填写路由路径'))
} else {
callback()
}
}
}],
component: [{
required: true,
validator: (rule, value, callback) => {
if (formData.menuType === 2 && !value) {
callback(new Error('菜单类型必须填写组件路径'))
} else {
callback()
}
}
}],
permission: [{
validator: (rule, value, callback) => {
if (formData.menuType === 3 && !value) {
callback(new Error('按钮类型必须填写权限标识'))
} else {
callback()
}
}
}]
}
```
## Data Models
### 表单数据模型
```typescript
interface MenuFormData {
id: number
parentId: number
name: string
path: string
component: string
icon: string
menuType: number
permission: string
sortOrder: number
status: number
isExternal: boolean
isCache: boolean
}
// 默认值
const defaultFormData: MenuFormData = {
id: 0,
parentId: 0,
name: '',
path: '',
component: '',
icon: '',
menuType: 2,
permission: '',
sortOrder: 0,
status: 1,
isExternal: false,
isCache: true
}
```
## Correctness Properties
*A property is a characteristic or behavior that should hold true across all valid executions of a system.*
本功能主要是 UI 交互,大部分验证在后端完成。以下是可测试的属性:
**Property 1: 表单提交数据完整性**
*For any* valid form submission, the request payload SHALL contain all required fields based on menuType.
**Validates: Requirements 6.1, 6.2, 6.3**
**Property 2: 父菜单选择排除自身**
*For any* menu being edited, the parent menu selector SHALL NOT include the menu itself or its descendants.
**Validates: Requirements 4.3**
## Error Handling
| 场景 | 处理方式 |
|------|----------|
| API 请求失败 | 显示 ElMessage.error 提示错误信息 |
| 表单验证失败 | 阻止提交,显示字段错误提示 |
| 删除有子菜单的菜单 | 后端返回错误,前端显示提示 |
| 网络超时 | 显示网络错误提示 |
## Testing Strategy
由于本功能主要是 UI 交互,测试策略以手动测试为主:
1. **功能测试**:验证新增、编辑、删除操作正常工作
2. **表单验证测试**:验证必填字段和条件必填逻辑
3. **边界测试**:测试空数据、特殊字符等边界情况