| import { useNextFetchKnowledgeList } from '@/hooks/knowledge-hooks'; |
| import { UserOutlined } from '@ant-design/icons'; |
| import type { TreeDataNode, TreeProps } from 'antd'; |
| import { Avatar, Layout, Space, Spin, Tree, Typography } from 'antd'; |
| import classNames from 'classnames'; |
| import { |
| Dispatch, |
| SetStateAction, |
| useCallback, |
| useEffect, |
| useMemo, |
| useState, |
| } from 'react'; |
|
|
| import styles from './index.less'; |
|
|
| const { Sider } = Layout; |
|
|
| interface IProps { |
| isFirstRender: boolean; |
| checkedList: string[]; |
| setCheckedList: Dispatch<SetStateAction<string[]>>; |
| } |
|
|
| const SearchSidebar = ({ |
| isFirstRender, |
| checkedList, |
| setCheckedList, |
| }: IProps) => { |
| const { list, loading } = useNextFetchKnowledgeList(); |
|
|
| const groupedList = useMemo(() => { |
| return list.reduce((pre: TreeDataNode[], cur) => { |
| const parentItem = pre.find((x) => x.key === cur.embd_id); |
| const childItem: TreeDataNode = { |
| title: cur.name, |
| key: cur.id, |
| isLeaf: true, |
| }; |
| if (parentItem) { |
| parentItem.children?.push(childItem); |
| } else { |
| pre.push({ |
| title: cur.embd_id, |
| key: cur.embd_id, |
| isLeaf: false, |
| children: [childItem], |
| }); |
| } |
|
|
| return pre; |
| }, []); |
| }, [list]); |
|
|
| const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]); |
| const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]); |
| const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true); |
|
|
| const onExpand: TreeProps['onExpand'] = (expandedKeysValue) => { |
| |
| |
| setExpandedKeys(expandedKeysValue); |
| setAutoExpandParent(false); |
| }; |
|
|
| const onCheck: TreeProps['onCheck'] = (checkedKeysValue, info) => { |
| console.log('onCheck', checkedKeysValue, info); |
| const currentCheckedKeysValue = checkedKeysValue as string[]; |
|
|
| let nextSelectedKeysValue: string[] = []; |
| const { isLeaf, checked, key, children } = info.node; |
| if (isLeaf) { |
| const item = list.find((x) => x.id === key); |
| if (!checked) { |
| const embeddingIds = currentCheckedKeysValue |
| .filter((x) => list.some((y) => y.id === x)) |
| .map((x) => list.find((y) => y.id === x)?.embd_id); |
|
|
| if (embeddingIds.some((x) => x !== item?.embd_id)) { |
| nextSelectedKeysValue = [key as string]; |
| } else { |
| nextSelectedKeysValue = currentCheckedKeysValue; |
| } |
| } else { |
| nextSelectedKeysValue = currentCheckedKeysValue; |
| } |
| } else { |
| if (!checked) { |
| nextSelectedKeysValue = [ |
| key as string, |
| ...(children?.map((x) => x.key as string) ?? []), |
| ]; |
| } else { |
| nextSelectedKeysValue = []; |
| } |
| } |
|
|
| setCheckedList(nextSelectedKeysValue); |
| }; |
|
|
| const onSelect: TreeProps['onSelect'] = (selectedKeysValue, info) => { |
| console.log('onSelect', info); |
|
|
| setSelectedKeys(selectedKeysValue); |
| }; |
|
|
| const renderTitle = useCallback( |
| (node: TreeDataNode) => { |
| const item = list.find((x) => x.id === node.key); |
| return ( |
| <Space> |
| {node.isLeaf && ( |
| <Avatar size={24} icon={<UserOutlined />} src={item?.avatar} /> |
| )} |
| <Typography.Text |
| ellipsis={{ tooltip: node.title as string }} |
| className={node.isLeaf ? styles.knowledgeName : styles.embeddingId} |
| > |
| {node.title as string} |
| </Typography.Text> |
| </Space> |
| ); |
| }, |
| [list], |
| ); |
|
|
| useEffect(() => { |
| const firstGroup = groupedList[0]?.children?.map((x) => x.key as string); |
| if (firstGroup) { |
| setCheckedList(firstGroup); |
| } |
| setExpandedKeys(groupedList.map((x) => x.key)); |
| }, [groupedList, setExpandedKeys, setCheckedList]); |
|
|
| return ( |
| <Sider |
| className={classNames(styles.searchSide, { |
| [styles.transparentSearchSide]: isFirstRender, |
| })} |
| theme={'light'} |
| width={'20%'} |
| > |
| <Spin spinning={loading}> |
| <Tree |
| className={styles.list} |
| checkable |
| onExpand={onExpand} |
| expandedKeys={expandedKeys} |
| autoExpandParent={autoExpandParent} |
| onCheck={onCheck} |
| checkedKeys={checkedList} |
| onSelect={onSelect} |
| selectedKeys={selectedKeys} |
| treeData={groupedList} |
| titleRender={renderTitle} |
| /> |
| </Spin> |
| </Sider> |
| ); |
| }; |
|
|
| export default SearchSidebar; |
|
|