120 lines
3.2 KiB
React
120 lines
3.2 KiB
React
|
|
import { Tree, Tag, Divider, Button, Space } from 'antd'
|
|||
|
|
import { useState, useEffect } from 'react'
|
|||
|
|
import './TreeFilterPanel.css'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 树形筛选面板组件
|
|||
|
|
* @param {Object} props
|
|||
|
|
* @param {Array} props.treeData - 树形数据
|
|||
|
|
* @param {string} props.selectedKey - 当前选中的节点ID
|
|||
|
|
* @param {string} props.tempSelectedKey - 临时选中的节点ID(确认前)
|
|||
|
|
* @param {string} props.treeTitle - 树标题
|
|||
|
|
* @param {Function} props.onSelect - 选择变化回调
|
|||
|
|
* @param {Function} props.onConfirm - 确认筛选
|
|||
|
|
* @param {Function} props.onClear - 清除筛选
|
|||
|
|
* @param {string} props.placeholder - 占位提示文本
|
|||
|
|
*/
|
|||
|
|
function TreeFilterPanel({
|
|||
|
|
treeData,
|
|||
|
|
selectedKey,
|
|||
|
|
tempSelectedKey,
|
|||
|
|
treeTitle = '分组筛选',
|
|||
|
|
onSelect,
|
|||
|
|
onConfirm,
|
|||
|
|
onClear,
|
|||
|
|
placeholder = '请选择分组进行筛选',
|
|||
|
|
}) {
|
|||
|
|
// 获取所有节点的key用于默认展开
|
|||
|
|
const getAllKeys = (nodes) => {
|
|||
|
|
let keys = []
|
|||
|
|
const traverse = (node) => {
|
|||
|
|
keys.push(node.key)
|
|||
|
|
if (node.children) {
|
|||
|
|
node.children.forEach(traverse)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
nodes.forEach(traverse)
|
|||
|
|
return keys
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const [expandedKeys, setExpandedKeys] = useState([])
|
|||
|
|
|
|||
|
|
// 初始化时展开所有节点
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (treeData && treeData.length > 0) {
|
|||
|
|
setExpandedKeys(getAllKeys(treeData))
|
|||
|
|
}
|
|||
|
|
}, [treeData])
|
|||
|
|
|
|||
|
|
// 查找节点名称
|
|||
|
|
const findNodeName = (nodes, id) => {
|
|||
|
|
for (const node of nodes) {
|
|||
|
|
if (node.key === id) return node.title
|
|||
|
|
if (node.children) {
|
|||
|
|
const found = findNodeName(node.children, id)
|
|||
|
|
if (found) return found
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ''
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleTreeSelect = (selectedKeys) => {
|
|||
|
|
const key = selectedKeys[0] || null
|
|||
|
|
onSelect?.(key)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleExpand = (keys) => {
|
|||
|
|
setExpandedKeys(keys)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="tree-filter-panel">
|
|||
|
|
{/* 已选择的筛选条件 */}
|
|||
|
|
<div className="tree-filter-selected">
|
|||
|
|
{tempSelectedKey ? (
|
|||
|
|
<div className="tree-filter-tag">
|
|||
|
|
<span className="tree-filter-label">已选择分组:</span>
|
|||
|
|
<Tag color="blue" closable onClose={() => onSelect?.(null)}>
|
|||
|
|
{findNodeName(treeData, tempSelectedKey)}
|
|||
|
|
</Tag>
|
|||
|
|
</div>
|
|||
|
|
) : (
|
|||
|
|
<div className="tree-filter-placeholder">
|
|||
|
|
<span>{placeholder}</span>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Divider style={{ margin: '12px 0' }} />
|
|||
|
|
|
|||
|
|
{/* 树形选择器 */}
|
|||
|
|
<div className="tree-filter-container">
|
|||
|
|
<div className="tree-filter-header">{treeTitle}</div>
|
|||
|
|
<Tree
|
|||
|
|
treeData={treeData}
|
|||
|
|
expandedKeys={expandedKeys}
|
|||
|
|
onExpand={handleExpand}
|
|||
|
|
onSelect={handleTreeSelect}
|
|||
|
|
selectedKeys={tempSelectedKey ? [tempSelectedKey] : []}
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Divider style={{ margin: '12px 0' }} />
|
|||
|
|
|
|||
|
|
{/* 操作按钮 */}
|
|||
|
|
<div className="tree-filter-actions">
|
|||
|
|
<Space>
|
|||
|
|
<Button size="small" onClick={onClear}>
|
|||
|
|
清除筛选
|
|||
|
|
</Button>
|
|||
|
|
<Button size="small" type="primary" onClick={onConfirm}>
|
|||
|
|
确定
|
|||
|
|
</Button>
|
|||
|
|
</Space>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default TreeFilterPanel
|