--- name: jjb-category-tree description: 定义分类树组件规范。在使用左侧树形分类导航、分类搜索、右键菜单操作时使用。 --- # 页面布局与通用 UI 规范 ## 分类树组件 分类树用于左侧导航区域,提供树形结构的浏览、搜索、右键菜单操作等功能。 ### 布局与宽度 - 分类树作为左侧面板,**固定宽度**,通常为 **240px ~ 260px**,高度撑满父容器。 - 树内容区 `flex: 1; overflow: auto`,节点过多时可垂直滚动。 - 结构分区(自上而下): 1. **标题栏**:左侧显示标题文字(如"文件夹"),右侧放置新建图标按钮。 2. **搜索栏**:输入框带搜索前缀图标,`size="small"`,支持 `allowClear`。 3. **树主体**:`Spin` 包裹 `Tree` 组件,空数据时显示"暂无数据"占位提示。 ```less .category-tree { display: flex; flex-direction: column; height: 100%; .category-tree-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 12px 8px; } .category-tree-search { padding: 0 12px 8px; } .category-tree-body { flex: 1; overflow: auto; padding: 0 8px; } } ``` ### 树节点渲染 - 使用 `titleRender` 自定义节点内容,组合**高亮文本**与**悬停操作图标**。 - 使用 `fieldNames` 将后端字段映射为 Tree 所需格式(`title` → 显示名、`key` → 唯一标识、`children` → 子节点)。 - 设置 `blockNode` 使整行可点击,提升操作区域。 - 节点标题文本需设置溢出省略: ```less .tree-node-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } ``` ```jsx (
{highlight(nodeData.folderName, searchValue)} { e.stopPropagation(); handleCreate(nodeData.id); }} /> { e.stopPropagation(); handleRename(nodeData); }} /> { e.stopPropagation(); handleDelete(nodeData); }} style={{ color: '#ff4d4f' }} />
)} /> ``` > **注意**:操作图标的 `onClick` 必须 `e.stopPropagation()` 阻止冒泡,避免触发树节点选中。 ### 选中态与悬停交互 - 选中节点背景色使用浅蓝 `#e6f4ff`。 - 悬停时显示行内操作图标,默认隐藏,通过 CSS 控制: ```less .tree-node-actions { display: none; flex-shrink: 0; } :global { .ant-tree-treenode:hover .tree-node-actions { display: inline-flex; } .ant-tree-treenode-selected .ant-tree-node-content-wrapper { background: #e6f4ff; } } ``` - 节点内边距不宜过大,保持紧凑:`.ant-tree-node-content-wrapper { padding: 2px 4px; }`。 ### 搜索匹配文字高亮 - 在搜索框中输入关键词后,树节点中**匹配的文字**需高亮显示。 - 高亮颜色使用主题蓝色 `#0064C8`。 - 实现方式:将节点标题文本按关键词拆分,匹配部分用 `` 包裹。 - 关键词中的正则特殊字符需转义处理。 ```jsx function highlight(text, keyword) { if (!keyword) return text; const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp(`(${escaped})`, 'gi'); return text.split(regex).map((part, i) => regex.test(part) ? {part} : part ); } ``` - 搜索结果需 **过滤树**:保留标题匹配的节点,以及其子孙中有匹配的父节点(保持树结构)。 - 搜索有值时,应**自动展开所有匹配节点**,让用户直接看到结果。 ### 右键菜单操作 - 使用 `Dropdown` + `menu` + `trigger={['contextMenu']}` 实现右键弹出菜单。 - `Dropdown` 包裹整个 `Tree` 组件,`onRightClick` 记录当前右键节点。 - 菜单项通常包括:**新建子文件夹**、**重命名**、**删除**(删除项标记 `danger: true`)。 - 删除操作需弹出二次确认弹窗(`Modal.confirm`),提示删除后不可恢复。 ```jsx const contextMenuItems = [ { key: 'create', label: '新建子文件夹' }, { key: 'rename', label: '重命名' }, { key: 'delete', label: '删除', danger: true }, ]; setRightClickNode(node)} // ... /> ``` - 右键新建子文件夹时,`parentId` 为右键节点的 id;标题栏新建按钮点击时,`parentId` 为当前选中节点 id(无选中则传 `null` 表示根级新建)。 ### 新增/重命名弹窗 - 使用 `Modal` + `Input` 实现新增和重命名,两种操作复用同一弹窗,通过状态区分标题文案。 - 输入框绑定 `onPressEnter` 支持回车确认。 - 名称需 `trim()` 处理,空值不允许提交。 - Modal 的 `confirmLoading` 绑定提交 loading 状态,防止重复点击。 ```jsx setModalOpen(false)} confirmLoading={submitLoading} destroyOnHidden > setModalName(e.target.value)} onPressEnter={handleModalOk} /> ``` > `destroyOnHidden` 确保每次打开弹窗时输入框状态被重置。 ### Loading 状态 - 树数据加载时:使用 `Spin` 组件包裹树主体,绑定树数据的 loading 状态。 - 提交操作时:Modal 的 `confirmLoading` 绑定提交 loading,防止重复提交。 - 操作完成后需**重新请求树数据**刷新列表。 ```jsx
{filteredTree.length > 0 ? :
暂无文件夹
}
``` ### 空状态 - 树数据为空或搜索无结果时,显示居中占位文字"暂无文件夹"。 - 样式:`text-align: center; color: #999; padding: 24px 0; font-size: 13px;`。