|
|
@ -48,7 +48,7 @@ interface ResourceNode { |
|
|
|
|
|
|
|
|
// —— catalog/menu 专用 —— |
|
|
// —— catalog/menu 专用 —— |
|
|
icon?: string; // 图标预览 URL |
|
|
icon?: string; // 图标预览 URL |
|
|
} |
|
|
children?: ResourceNode[]; // 子节点列表(listAll 返回时填充,其他接口无此字段) |
|
|
``` |
|
|
``` |
|
|
|
|
|
|
|
|
> ⚠️ **ID 一律按字符串收发**。后端返回的 id 是字符串(雪花 ID 超出 JS Number 安全范围),前端回传原样传字符串即可,不要转 Number。 |
|
|
> ⚠️ **ID 一律按字符串收发**。后端返回的 id 是字符串(雪花 ID 超出 JS Number 安全范围),前端回传原样传字符串即可,不要转 Number。 |
|
|
@ -89,7 +89,7 @@ GET /api/resources/list |
|
|
Authorization: Bearer {token} |
|
|
Authorization: Bearer {token} |
|
|
``` |
|
|
``` |
|
|
|
|
|
|
|
|
**响应示例**: |
|
|
**响应示例**(后端已组树,直接渲染即可): |
|
|
|
|
|
|
|
|
```json |
|
|
```json |
|
|
{ |
|
|
{ |
|
|
@ -103,59 +103,44 @@ Authorization: Bearer {token} |
|
|
"type": "CATALOG", |
|
|
"type": "CATALOG", |
|
|
"name": "系统管理", |
|
|
"name": "系统管理", |
|
|
"sort": 1, |
|
|
"sort": 1, |
|
|
"description": null, |
|
|
"icon": "https://cdn.example.com/icons/system.png", |
|
|
"icon": "https://cdn.example.com/icons/system.png" |
|
|
"children": [ |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
"id": "10", |
|
|
"id": "10", |
|
|
"parentId": "1", |
|
|
"parentId": "1", |
|
|
"type": "MENU", |
|
|
"type": "MENU", |
|
|
"name": "用户管理", |
|
|
"name": "用户管理", |
|
|
"sort": 1, |
|
|
"sort": 1, |
|
|
"route": "/system/user", |
|
|
"route": "/system/user", |
|
|
"children": [ |
|
|
"icon": null |
|
|
{ |
|
|
}, |
|
|
"id": "100", |
|
|
{ |
|
|
"parentId": "10", |
|
|
"id": "100", |
|
|
"type": "BUTTON", |
|
|
"parentId": "10", |
|
|
"name": "查询用户", |
|
|
"type": "BUTTON", |
|
|
"sort": 1, |
|
|
"name": "查询用户", |
|
|
"perms": "crm:user:list", |
|
|
"sort": 1, |
|
|
"denyBehavior": "hide", |
|
|
"perms": "crm:user:list", |
|
|
"apiUrl": "/api/system/users/page", |
|
|
"denyBehavior": "hide", |
|
|
"status": "enabled", |
|
|
"apiUrl": "/api/system/users/page", |
|
|
"description": "查询用户列表", |
|
|
"status": "enabled", |
|
|
"children": [] |
|
|
"description": "查询用户列表" |
|
|
} |
|
|
|
|
|
] |
|
|
|
|
|
} |
|
|
|
|
|
] |
|
|
} |
|
|
} |
|
|
] |
|
|
] |
|
|
} |
|
|
} |
|
|
``` |
|
|
``` |
|
|
|
|
|
|
|
|
**前端处理**:后端返回扁平数组,前端通过 `parentId` 自行组树。 |
|
|
**前端处理**:后端已通过 `TreeUtils.buildTree` 组好树,`data` 直接就是树形结构,无需前端再按 `parentId` 组树。直接渲染即可: |
|
|
|
|
|
|
|
|
```typescript |
|
|
```typescript |
|
|
// 组树示例 |
|
|
// 直接渲染后端返回的树形数据 |
|
|
function buildTree(nodes: ResourceNode[]): TreeNode[] { |
|
|
const tree = response.data; // 已是树形结构 |
|
|
const map = new Map<string, TreeNode>(); |
|
|
|
|
|
const roots: TreeNode[] = []; |
|
|
|
|
|
|
|
|
|
|
|
nodes.forEach(node => { |
|
|
<Tree treeData={tree} /> |
|
|
map.set(node.id!, { ...node, children: [] }); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
nodes.forEach(node => { |
|
|
|
|
|
const treeNode = map.get(node.id!)!; |
|
|
|
|
|
const parentId = node.parentId || "0"; |
|
|
|
|
|
if (parentId === "0") { |
|
|
|
|
|
roots.push(treeNode); |
|
|
|
|
|
} else { |
|
|
|
|
|
const parent = map.get(parentId); |
|
|
|
|
|
parent?.children.push(treeNode); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
return roots.sort((a, b) => a.sort - b.sort); |
|
|
|
|
|
} |
|
|
|
|
|
``` |
|
|
``` |
|
|
|
|
|
|
|
|
--- |
|
|
--- |
|
|
@ -538,7 +523,7 @@ curl -X POST "{baseUrl}/api/resources/delete" \ |
|
|
|
|
|
|
|
|
### 6.3 前端开发建议 |
|
|
### 6.3 前端开发建议 |
|
|
|
|
|
|
|
|
1. **树形组件**:推荐使用 `parentId` 在前端组树,后端返回扁平数组性能更好 |
|
|
1. **树形数据**:后端已组好树并按 sort 排序,前端直接渲染 `data` 即可,无需自行组树 |
|
|
2. **表单提交**:使用 `URLSearchParams` 或 `qs` 库构造表单字段,不要用 `JSON.stringify` 整体打包 |
|
|
2. **表单提交**:使用 `URLSearchParams` 或 `qs` 库构造表单字段,不要用 `JSON.stringify` 整体打包 |
|
|
3. **ID 处理**:所有 id 字段按字符串处理,不要 `parseInt` / `Number()` |
|
|
3. **ID 处理**:所有 id 字段按字符串处理,不要 `parseInt` / `Number()` |
|
|
4. **表单校验**:根据节点类型动态显示必填字段 |
|
|
4. **表单校验**:根据节点类型动态显示必填字段 |
|
|
|